Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Side by Side Diff: webkit/port/bindings/v8/v8_proxy.cpp

Issue 149086: Use upstream V8Proxy and V8Utilities (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « webkit/port/bindings/v8/v8_proxy.h ('k') | webkit/port/bindings/v8/v8_utility.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2008, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "config.h"
31
32 #include <algorithm>
33 #include <utility>
34
35 #include <v8.h>
36 #include <v8-debug.h>
37
38 #include "v8_proxy.h"
39 #include "v8_binding.h"
40 #include "V8Collection.h"
41 #include "V8DOMWindow.h"
42 #include "V8IsolatedWorld.h"
43
44 #include "ChromiumBridge.h"
45 #include "CSSMutableStyleDeclaration.h"
46 #include "DOMObjectsInclude.h"
47 #include "DocumentLoader.h"
48 #include "ScriptController.h"
49 #include "V8CustomBinding.h"
50 #include "V8DOMMap.h"
51 #include "V8Index.h"
52 #include "WorkerContextExecutionProxy.h"
53
54 namespace WebCore {
55
56 // Static utility context.
57 v8::Persistent<v8::Context> V8Proxy::m_utilityContext;
58
59 // Static list of registered extensions
60 V8ExtensionList V8Proxy::m_extensions;
61
62 // static
63 const char* V8Proxy::kContextDebugDataType = "type";
64 const char* V8Proxy::kContextDebugDataValue = "value";
65
66 #ifndef NDEBUG
67 // Keeps track of global handles created (not JS wrappers
68 // of DOM objects). Often these global handles are source
69 // of leaks.
70 //
71 // If you want to let a C++ object hold a persistent handle
72 // to a JS object, you should register the handle here to
73 // keep track of leaks.
74 //
75 // When creating a persistent handle, call:
76 //
77 // #ifndef NDEBUG
78 // V8Proxy::RegisterGlobalHandle(type, host, handle);
79 // #endif
80 //
81 // When releasing the handle, call:
82 //
83 // #ifndef NDEBUG
84 // V8Proxy::UnregisterGlobalHandle(type, host, handle);
85 // #endif
86 //
87 typedef HashMap<v8::Value*, GlobalHandleInfo*> GlobalHandleMap;
88
89 static GlobalHandleMap& global_handle_map()
90 {
91 static GlobalHandleMap static_global_handle_map;
92 return static_global_handle_map;
93 }
94
95
96 // The USE_VAR(x) template is used to silence C++ compiler warnings
97 // issued for unused variables (typically parameters or values that
98 // we want to watch in the debugger).
99 template <typename T>
100 static inline void USE_VAR(T) { }
101
102 // The function is the place to set the break point to inspect
103 // live global handles. Leaks are often come from leaked global handles.
104 static void EnumerateGlobalHandles()
105 {
106 for (GlobalHandleMap::iterator it = global_handle_map().begin(),
107 end = global_handle_map().end(); it != end; ++it) {
108 GlobalHandleInfo* info = it->second;
109 USE_VAR(info);
110 v8::Value* handle = it->first;
111 USE_VAR(handle);
112 }
113 }
114
115 void V8Proxy::RegisterGlobalHandle(GlobalHandleType type, void* host,
116 v8::Persistent<v8::Value> handle)
117 {
118 ASSERT(!global_handle_map().contains(*handle));
119 global_handle_map().set(*handle, new GlobalHandleInfo(host, type));
120 }
121
122
123 void V8Proxy::UnregisterGlobalHandle(void* host, v8::Persistent<v8::Value> handl e)
124 {
125 ASSERT(global_handle_map().contains(*handle));
126 GlobalHandleInfo* info = global_handle_map().take(*handle);
127 ASSERT(info->host_ == host);
128 delete info;
129 }
130 #endif // ifndef NDEBUG
131
132 void BatchConfigureAttributes(v8::Handle<v8::ObjectTemplate> inst,
133 v8::Handle<v8::ObjectTemplate> proto,
134 const BatchedAttribute* attrs,
135 size_t num_attrs)
136 {
137 for (size_t i = 0; i < num_attrs; ++i) {
138 const BatchedAttribute* a = &attrs[i];
139 (a->on_proto ? proto : inst)->SetAccessor(
140 v8::String::New(a->name),
141 a->getter,
142 a->setter,
143 a->data == V8ClassIndex::INVALID_CLASS_INDEX
144 ? v8::Handle<v8::Value>()
145 : v8::Integer::New(V8ClassIndex::ToInt(a->data)),
146 a->settings,
147 a->attribute);
148 }
149 }
150
151 void BatchConfigureConstants(v8::Handle<v8::FunctionTemplate> desc,
152 v8::Handle<v8::ObjectTemplate> proto,
153 const BatchedConstant* consts,
154 size_t num_consts)
155 {
156 for (size_t i = 0; i < num_consts; ++i) {
157 const BatchedConstant* c = &consts[i];
158 desc->Set(v8::String::New(c->name),
159 v8::Integer::New(c->value),
160 v8::ReadOnly);
161 proto->Set(v8::String::New(c->name),
162 v8::Integer::New(c->value),
163 v8::ReadOnly);
164 }
165 }
166
167 typedef HashMap<Node*, v8::Object*> DOMNodeMap;
168 typedef HashMap<void*, v8::Object*> DOMObjectMap;
169
170 #ifndef NDEBUG
171
172 static void EnumerateDOMObjectMap(DOMObjectMap& wrapper_map)
173 {
174 for (DOMObjectMap::iterator it = wrapper_map.begin(), end = wrapper_map.end();
175 it != end; ++it) {
176 v8::Persistent<v8::Object> wrapper(it->second);
177 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper);
178 void* obj = it->first;
179 USE_VAR(type);
180 USE_VAR(obj);
181 }
182 }
183
184 class DOMObjectVisitor : public DOMWrapperMap<void>::Visitor {
185 public:
186 void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper) {
187 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper);
188 USE_VAR(type);
189 USE_VAR(object);
190 }
191 };
192
193 class EnsureWeakDOMNodeVisitor : public DOMWrapperMap<Node>::Visitor {
194 public:
195 void visitDOMWrapper(Node* object, v8::Persistent<v8::Object> wrapper) {
196 USE_VAR(object);
197 ASSERT(wrapper.IsWeak());
198 }
199 };
200
201 #endif // NDEBUG
202
203 #if ENABLE(SVG)
204 v8::Handle<v8::Value> V8Proxy::SVGElementInstanceToV8Object(
205 SVGElementInstance* instance)
206 {
207 if (!instance)
208 return v8::Null();
209
210 v8::Handle<v8::Object> existing_instance = getDOMSVGElementInstanceMap().get(i nstance);
211 if (!existing_instance.IsEmpty())
212 return existing_instance;
213
214 instance->ref();
215
216 // Instantiate the V8 object and remember it
217 v8::Handle<v8::Object> result =
218 InstantiateV8Object(V8ClassIndex::SVGELEMENTINSTANCE,
219 V8ClassIndex::SVGELEMENTINSTANCE,
220 instance);
221 if (!result.IsEmpty()) {
222 // Only update the DOM SVG element map if the result is non-empty.
223 getDOMSVGElementInstanceMap().set(instance,
224 v8::Persistent<v8::Object>::New(result));
225 }
226 return result;
227 }
228
229 // Map of SVG objects with contexts to their contexts
230 static HashMap<void*, SVGElement*>& svg_object_to_context_map()
231 {
232 static HashMap<void*, SVGElement*> static_svg_object_to_context_map;
233 return static_svg_object_to_context_map;
234 }
235
236 v8::Handle<v8::Value> V8Proxy::SVGObjectWithContextToV8Object(
237 V8ClassIndex::V8WrapperType type, void* object)
238 {
239 if (!object)
240 return v8::Null();
241
242 v8::Persistent<v8::Object> result =
243 getDOMSVGObjectWithContextMap().get(object);
244 if (!result.IsEmpty()) return result;
245
246 // Special case: SVGPathSegs need to be downcast to their real type
247 if (type == V8ClassIndex::SVGPATHSEG)
248 type = V8Custom::DowncastSVGPathSeg(object);
249
250 v8::Local<v8::Object> v8obj = InstantiateV8Object(type, type, object);
251 if (!v8obj.IsEmpty()) {
252 result = v8::Persistent<v8::Object>::New(v8obj);
253 switch (type) {
254 #define MAKE_CASE(TYPE, NAME) \
255 case V8ClassIndex::TYPE: static_cast<NAME*>(object)->ref(); break;
256 SVG_OBJECT_TYPES(MAKE_CASE)
257 #undef MAKE_CASE
258 #define MAKE_CASE(TYPE, NAME) \
259 case V8ClassIndex::TYPE: \
260 static_cast<V8SVGPODTypeWrapper<NAME>*>(object)->ref(); break;
261 SVG_POD_NATIVE_TYPES(MAKE_CASE)
262 #undef MAKE_CASE
263 default:
264 ASSERT(false);
265 }
266 getDOMSVGObjectWithContextMap().set(object, result);
267 }
268
269 return result;
270 }
271
272 void V8Proxy::SetSVGContext(void* obj, SVGElement* context)
273 {
274 if (obj == NULL)
275 return;
276
277 SVGElement* old_context = svg_object_to_context_map().get(obj);
278
279 if (old_context == context)
280 return;
281
282 if (old_context)
283 old_context->deref();
284
285 if (context)
286 context->ref();
287
288 svg_object_to_context_map().set(obj, context);
289 }
290
291 SVGElement* V8Proxy::GetSVGContext(void* obj)
292 {
293 return svg_object_to_context_map().get(obj);
294 }
295
296 #endif
297
298 // A map from a DOM node to its JS wrapper, the wrapper
299 // is kept as a strong reference to survive GCs.
300 static DOMObjectMap& gc_protected_map() {
301 static DOMObjectMap static_gc_protected_map;
302 return static_gc_protected_map;
303 }
304
305 // static
306 void V8Proxy::GCProtect(void* dom_object)
307 {
308 if (!dom_object)
309 return;
310 if (gc_protected_map().contains(dom_object))
311 return;
312 if (!getDOMObjectMap().contains(dom_object))
313 return;
314
315 // Create a new (strong) persistent handle for the object.
316 v8::Persistent<v8::Object> wrapper = getDOMObjectMap().get(dom_object);
317 if (wrapper.IsEmpty()) return;
318
319 gc_protected_map().set(dom_object, *v8::Persistent<v8::Object>::New(wrapper));
320 }
321
322
323 // static
324 void V8Proxy::GCUnprotect(void* dom_object)
325 {
326 if (!dom_object)
327 return;
328 if (!gc_protected_map().contains(dom_object))
329 return;
330
331 // Dispose the strong reference.
332 v8::Persistent<v8::Object> wrapper(gc_protected_map().take(dom_object));
333 wrapper.Dispose();
334 }
335
336 class GCPrologueVisitor : public DOMWrapperMap<void>::Visitor {
337 public:
338 void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper) {
339 ASSERT(wrapper.IsWeak());
340 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper);
341 switch (type) {
342 #define MAKE_CASE(TYPE, NAME) \
343 case V8ClassIndex::TYPE: { \
344 NAME* impl = static_cast<NAME*>(object); \
345 if (impl->hasPendingActivity()) \
346 wrapper.ClearWeak(); \
347 break; \
348 }
349 ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE)
350 default:
351 ASSERT(false);
352 #undef MAKE_CASE
353 }
354
355 // Additional handling of message port ensuring that entangled ports also
356 // have their wrappers entangled. This should ideally be handled when the
357 // ports are actually entangled in MessagePort::entangle, but to avoid
358 // forking MessagePort.* this is postponed to GC time. Having this postponed
359 // has the drawback that the wrappers are "entangled/unentangled" for each
360 // GC even though their entanglement most likely is still the same.
361 if (type == V8ClassIndex::MESSAGEPORT) {
362 // Get the port and its entangled port.
363 MessagePort* port1 = static_cast<MessagePort*>(object);
364 MessagePort* port2 = port1->locallyEntangledPort();
365
366 // If we are remotely entangled, then mark this object as reachable
367 // (we can't determine reachability directly as the remote object is
368 // out-of-proc).
369 if (port1->isEntangled() && !port2)
370 wrapper.ClearWeak();
371
372 if (port2 != NULL) {
373 // As ports are always entangled in pairs only perform the entanglement
374 // once for each pair (see ASSERT in MessagePort::unentangle()).
375 if (port1 < port2) {
376 v8::Handle<v8::Value> port1_wrapper =
377 V8Proxy::ToV8Object(V8ClassIndex::MESSAGEPORT, port1);
378 v8::Handle<v8::Value> port2_wrapper =
379 V8Proxy::ToV8Object(V8ClassIndex::MESSAGEPORT, port2);
380 ASSERT(port1_wrapper->IsObject());
381 v8::Handle<v8::Object>::Cast(port1_wrapper)->SetInternalField(
382 V8Custom::kMessagePortEntangledPortIndex, port2_wrapper);
383 ASSERT(port2_wrapper->IsObject());
384 v8::Handle<v8::Object>::Cast(port2_wrapper)->SetInternalField(
385 V8Custom::kMessagePortEntangledPortIndex, port1_wrapper);
386 }
387 } else {
388 // Remove the wrapper entanglement when a port is not entangled.
389 if (V8Proxy::DOMObjectHasJSWrapper(port1)) {
390 v8::Handle<v8::Value> wrapper =
391 V8Proxy::ToV8Object(V8ClassIndex::MESSAGEPORT, port1);
392 ASSERT(wrapper->IsObject());
393 v8::Handle<v8::Object>::Cast(wrapper)->SetInternalField(
394 V8Custom::kMessagePortEntangledPortIndex, v8::Undefined());
395 }
396 }
397 }
398 }
399 };
400
401 class GrouperItem {
402 public:
403 GrouperItem(uintptr_t group_id, Node* node, v8::Persistent<v8::Object> wrapper )
404 : group_id_(group_id), node_(node), wrapper_(wrapper) { }
405
406 uintptr_t group_id() const { return group_id_; }
407 Node* node() const { return node_; }
408 v8::Persistent<v8::Object> wrapper() const { return wrapper_; }
409
410 private:
411 uintptr_t group_id_;
412 Node* node_;
413 v8::Persistent<v8::Object> wrapper_;
414 };
415
416 bool operator<(const GrouperItem& a, const GrouperItem& b) {
417 return a.group_id() < b.group_id();
418 }
419
420 typedef Vector<GrouperItem> GrouperList;
421
422 class ObjectGrouperVisitor : public DOMWrapperMap<Node>::Visitor {
423 public:
424 ObjectGrouperVisitor() {
425 // TODO(abarth): grouper_.reserveCapacity(node_map.size()); ?
426 }
427
428 void visitDOMWrapper(Node* node, v8::Persistent<v8::Object> wrapper) {
429 // If the node is in document, put it in the ownerDocument's object group.
430 //
431 // If an image element was created by JavaScript "new Image",
432 // it is not in a document. However, if the load event has not
433 // been fired (still onloading), it is treated as in the document.
434 //
435 // Otherwise, the node is put in an object group identified by the root
436 // elment of the tree to which it belongs.
437 uintptr_t group_id;
438 if (node->inDocument() ||
439 (node->hasTagName(HTMLNames::imgTag) &&
440 !static_cast<HTMLImageElement*>(node)->haveFiredLoadEvent())) {
441 group_id = reinterpret_cast<uintptr_t>(node->document());
442 } else {
443 Node* root = node;
444 while (root->parent())
445 root = root->parent();
446
447 // If the node is alone in its DOM tree (doesn't have a parent or any
448 // children) then the group will be filtered out later anyway.
449 if (root == node && !node->hasChildNodes())
450 return;
451
452 group_id = reinterpret_cast<uintptr_t>(root);
453 }
454 grouper_.append(GrouperItem(group_id, node, wrapper));
455 }
456
457 void ApplyGrouping() {
458 // Group by sorting by the group id.
459 std::sort(grouper_.begin(), grouper_.end());
460
461 // TODO(deanm): Should probably work in iterators here, but indexes were
462 // easier for my simple mind.
463 for (size_t i = 0; i < grouper_.size(); ) {
464 // Seek to the next key (or the end of the list).
465 size_t next_key_index = grouper_.size();
466 for (size_t j = i; j < grouper_.size(); ++j) {
467 if (grouper_[i].group_id() != grouper_[j].group_id()) {
468 next_key_index = j;
469 break;
470 }
471 }
472
473 ASSERT(next_key_index > i);
474
475 // We only care about a group if it has more than one object. If it only
476 // has one object, it has nothing else that needs to be kept alive.
477 if (next_key_index - i <= 1) {
478 i = next_key_index;
479 continue;
480 }
481
482 Vector<v8::Persistent<v8::Value> > group;
483 group.reserveCapacity(next_key_index - i);
484 for (; i < next_key_index; ++i) {
485 Node* node = grouper_[i].node();
486 v8::Persistent<v8::Value> wrapper = grouper_[i].wrapper();
487 if (!wrapper.IsEmpty())
488 group.append(wrapper);
489 /* TODO(abarth): Re-enabled this code to avoid GCing these wrappers!
490 Currently this depends on looking up the wrapper
491 during a GC, but we don't know which isolated world
492 we're in, so it's unclear which map to look in...
493
494 // If the node is styled and there is a wrapper for the inline
495 // style declaration, we need to keep that style declaration
496 // wrapper alive as well, so we add it to the object group.
497 if (node->isStyledElement()) {
498 StyledElement* element = reinterpret_cast<StyledElement*>(node);
499 CSSStyleDeclaration* style = element->inlineStyleDecl();
500 if (style != NULL) {
501 wrapper = getDOMObjectMap().get(style);
502 if (!wrapper.IsEmpty())
503 group.append(wrapper);
504 }
505 }
506 */
507 }
508
509 if (group.size() > 1)
510 v8::V8::AddObjectGroup(&group[0], group.size());
511
512 ASSERT(i == next_key_index);
513 }
514 }
515
516 private:
517 GrouperList grouper_;
518 };
519
520 // Create object groups for DOM tree nodes.
521 static void GCPrologue()
522 {
523 v8::HandleScope scope;
524
525 #ifndef NDEBUG
526 DOMObjectVisitor domObjectVisitor;
527 visitDOMObjectsInCurrentThread(&domObjectVisitor);
528 #endif
529
530 // Run through all objects with possible pending activity making their
531 // wrappers non weak if there is pending activity.
532 GCPrologueVisitor prologueVisitor;
533 visitActiveDOMObjectsInCurrentThread(&prologueVisitor);
534
535 // Create object groups.
536 ObjectGrouperVisitor objectGrouperVisitor;
537 visitDOMNodesInCurrentThread(&objectGrouperVisitor);
538 objectGrouperVisitor.ApplyGrouping();
539 }
540
541 class GCEpilogueVisitor : public DOMWrapperMap<void>::Visitor {
542 public:
543 void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
544 {
545 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper);
546 switch (type) {
547 #define MAKE_CASE(TYPE, NAME) \
548 case V8ClassIndex::TYPE: { \
549 NAME* impl = static_cast<NAME*>(object); \
550 if (impl->hasPendingActivity()) { \
551 ASSERT(!wrapper.IsWeak()); \
552 wrapper.MakeWeak(impl, &weakActiveDOMObjectCallback); \
553 } \
554 break; \
555 }
556 ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE)
557 default:
558 ASSERT(false);
559 #undef MAKE_CASE
560 }
561 }
562 };
563
564 static void GCEpilogue()
565 {
566 v8::HandleScope scope;
567
568 // Run through all objects with pending activity making their wrappers weak
569 // again.
570 GCEpilogueVisitor epilogueVisitor;
571 visitActiveDOMObjectsInCurrentThread(&epilogueVisitor);
572
573 #ifndef NDEBUG
574 // Check all survivals are weak.
575 DOMObjectVisitor domObjectVisitor;
576 visitDOMObjectsInCurrentThread(&domObjectVisitor);
577
578 EnsureWeakDOMNodeVisitor weakDOMNodeVisitor;
579 visitDOMNodesInCurrentThread(&weakDOMNodeVisitor);
580
581 EnumerateDOMObjectMap(gc_protected_map());
582 EnumerateGlobalHandles();
583 #undef USE_VAR
584 #endif
585 }
586
587
588 typedef HashMap<int, v8::FunctionTemplate*> FunctionTemplateMap;
589
590 bool AllowAllocation::m_current = false;
591
592
593 // JavaScriptConsoleMessages encapsulate everything needed to
594 // log messages originating from JavaScript to the Chrome console.
595 class JavaScriptConsoleMessage {
596 public:
597 JavaScriptConsoleMessage(const String& str,
598 const String& sourceID,
599 unsigned lineNumber)
600 : m_string(str)
601 , m_sourceID(sourceID)
602 , m_lineNumber(lineNumber) { }
603
604 void AddToPage(Page* page) const;
605
606 private:
607 const String m_string;
608 const String m_sourceID;
609 const unsigned m_lineNumber;
610 };
611
612 void JavaScriptConsoleMessage::AddToPage(Page* page) const
613 {
614 ASSERT(page);
615 Console* console = page->mainFrame()->domWindow()->console();
616 console->addMessage(JSMessageSource, ErrorMessageLevel, m_string, m_lineNumb er, m_sourceID);
617 }
618
619 // The ConsoleMessageManager handles all console messages that stem
620 // from JavaScript. It keeps a list of messages that have been delayed but
621 // it makes sure to add all messages to the console in the right order.
622 class ConsoleMessageManager {
623 public:
624 // Add a message to the console. May end up calling JavaScript code
625 // indirectly through the inspector so only call this function when
626 // it is safe to do allocations.
627 static void AddMessage(Page* page, const JavaScriptConsoleMessage& message);
628
629 // Add a message to the console but delay the reporting until it
630 // is safe to do so: Either when we leave JavaScript execution or
631 // when adding other console messages. The primary purpose of this
632 // method is to avoid calling into V8 to handle console messages
633 // when the VM is in a state that does not support GCs or allocations.
634 // Delayed messages are always reported in the page corresponding
635 // to the active context.
636 static void AddDelayedMessage(const JavaScriptConsoleMessage& message);
637
638 // Process any delayed messages. May end up calling JavaScript code
639 // indirectly through the inspector so only call this function when
640 // it is safe to do allocations.
641 static void ProcessDelayedMessages();
642
643 private:
644 // All delayed messages are stored in this vector. If the vector
645 // is NULL, there are no delayed messages.
646 static Vector<JavaScriptConsoleMessage>* m_delayed;
647 };
648
649
650 Vector<JavaScriptConsoleMessage>* ConsoleMessageManager::m_delayed = NULL;
651
652
653 void ConsoleMessageManager::AddMessage(
654 Page* page,
655 const JavaScriptConsoleMessage& message)
656 {
657 // Process any delayed messages to make sure that messages
658 // appear in the right order in the console.
659 ProcessDelayedMessages();
660 message.AddToPage(page);
661 }
662
663
664 void ConsoleMessageManager::AddDelayedMessage(const JavaScriptConsoleMessage& me ssage)
665 {
666 if (!m_delayed)
667 // Allocate a vector for the delayed messages. Will be
668 // deallocated when the delayed messages are processed
669 // in ProcessDelayedMessages().
670 m_delayed = new Vector<JavaScriptConsoleMessage>();
671 m_delayed->append(message);
672 }
673
674
675 void ConsoleMessageManager::ProcessDelayedMessages()
676 {
677 // If we have a delayed vector it cannot be empty.
678 if (!m_delayed)
679 return;
680 ASSERT(!m_delayed->isEmpty());
681
682 // Add the delayed messages to the page of the active
683 // context. If that for some bizarre reason does not
684 // exist, we clear the list of delayed messages to avoid
685 // posting messages. We still deallocate the vector.
686 Frame* frame = V8Proxy::retrieveFrameForEnteredContext();
687 Page* page = NULL;
688 if (frame)
689 page = frame->page();
690 if (!page)
691 m_delayed->clear();
692
693 // Iterate through all the delayed messages and add them
694 // to the console.
695 const int size = m_delayed->size();
696 for (int i = 0; i < size; i++) {
697 m_delayed->at(i).AddToPage(page);
698 }
699
700 // Deallocate the delayed vector.
701 delete m_delayed;
702 m_delayed = NULL;
703 }
704
705
706 // Convenience class for ensuring that delayed messages in the
707 // ConsoleMessageManager are processed quickly.
708 class ConsoleMessageScope {
709 public:
710 ConsoleMessageScope() { ConsoleMessageManager::ProcessDelayedMessages(); }
711 ~ConsoleMessageScope() { ConsoleMessageManager::ProcessDelayedMessages(); }
712 };
713
714 void log_info(Frame* frame, const String& msg, const String& url)
715 {
716 Page* page = frame->page();
717 if (!page)
718 return;
719 JavaScriptConsoleMessage message(msg, url, 0);
720 ConsoleMessageManager::AddMessage(page, message);
721 }
722
723 static void HandleConsoleMessage(v8::Handle<v8::Message> message,
724 v8::Handle<v8::Value> data)
725 {
726 // Use the frame where JavaScript is called from.
727 Frame* frame = V8Proxy::retrieveFrameForEnteredContext();
728 if (!frame)
729 return;
730
731 Page* page = frame->page();
732 if (!page)
733 return;
734
735 v8::Handle<v8::String> errorMessageString = message->Get();
736 ASSERT(!errorMessageString.IsEmpty());
737 String errorMessage = ToWebCoreString(errorMessageString);
738
739 v8::Handle<v8::Value> resourceName = message->GetScriptResourceName();
740 bool useURL = (resourceName.IsEmpty() || !resourceName->IsString());
741 String resourceNameString = (useURL)
742 ? frame->document()->url()
743 : ToWebCoreString(resourceName);
744 JavaScriptConsoleMessage consoleMessage(errorMessage,
745 resourceNameString,
746 message->GetLineNumber());
747 ConsoleMessageManager::AddMessage(page, consoleMessage);
748 }
749
750
751 enum DelayReporting {
752 REPORT_LATER,
753 REPORT_NOW
754 };
755
756
757 static void ReportUnsafeAccessTo(Frame* target, DelayReporting delay)
758 {
759 ASSERT(target);
760 Document* targetDocument = target->document();
761 if (!targetDocument)
762 return;
763
764 Frame* source = V8Proxy::retrieveFrameForEnteredContext();
765 if (!source || !source->document())
766 return; // Ignore error if the source document is gone.
767
768 Document* sourceDocument = source->document();
769
770 // FIXME: This error message should contain more specifics of why the same
771 // origin check has failed.
772 String str = String::format("Unsafe JavaScript attempt to access frame "
773 "with URL %s from frame with URL %s. "
774 "Domains, protocols and ports must match.\n",
775 targetDocument->url().string().utf8().data(),
776 sourceDocument->url().string().utf8().data());
777
778 // Build a console message with fake source ID and line number.
779 const String kSourceID = "";
780 const int kLineNumber = 1;
781 JavaScriptConsoleMessage message(str, kSourceID, kLineNumber);
782
783 if (delay == REPORT_NOW) {
784 // NOTE(tc): Apple prints the message in the target page, but it seems like
785 // it should be in the source page. Even for delayed messages, we put it in
786 // the source page; see ConsoleMessageManager::ProcessDelayedMessages().
787 ConsoleMessageManager::AddMessage(source->page(), message);
788
789 } else {
790 ASSERT(delay == REPORT_LATER);
791 // We cannot safely report the message eagerly, because this may cause
792 // allocations and GCs internally in V8 and we cannot handle that at this
793 // point. Therefore we delay the reporting.
794 ConsoleMessageManager::AddDelayedMessage(message);
795 }
796 }
797
798 static void ReportUnsafeJavaScriptAccess(v8::Local<v8::Object> host,
799 v8::AccessType type,
800 v8::Local<v8::Value> data)
801 {
802 Frame* target = V8Custom::GetTargetFrame(host, data);
803 if (target)
804 ReportUnsafeAccessTo(target, REPORT_LATER);
805 }
806
807 static void HandleFatalErrorInV8()
808 {
809 // TODO: We temporarily deal with V8 internal error situations
810 // such as out-of-memory by crashing the renderer.
811 CRASH();
812 }
813
814 static void ReportFatalErrorInV8(const char* location, const char* message)
815 {
816 // V8 is shutdown, we cannot use V8 api.
817 // The only thing we can do is to disable JavaScript.
818 // TODO: clean up V8Proxy and disable JavaScript.
819 printf("V8 error: %s (%s)\n", message, location);
820 HandleFatalErrorInV8();
821 }
822
823 V8Proxy::~V8Proxy()
824 {
825 clearForClose();
826 DestroyGlobal();
827 }
828
829 void V8Proxy::DestroyGlobal()
830 {
831 if (!m_global.IsEmpty()) {
832 #ifndef NDEBUG
833 UnregisterGlobalHandle(this, m_global);
834 #endif
835 m_global.Dispose();
836 m_global.Clear();
837 }
838 }
839
840
841 bool V8Proxy::DOMObjectHasJSWrapper(void* obj) {
842 return getDOMObjectMap().contains(obj) ||
843 getActiveDOMObjectMap().contains(obj);
844 }
845
846
847 // The caller must have increased obj's ref count.
848 void V8Proxy::SetJSWrapperForDOMObject(void* obj, v8::Persistent<v8::Object> wra pper)
849 {
850 ASSERT(MaybeDOMWrapper(wrapper));
851 #ifndef NDEBUG
852 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper);
853 switch (type) {
854 #define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE:
855 ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE)
856 ASSERT(false);
857 #undef MAKE_CASE
858 default: break;
859 }
860 #endif
861 getDOMObjectMap().set(obj, wrapper);
862 }
863
864 // The caller must have increased obj's ref count.
865 void V8Proxy::SetJSWrapperForActiveDOMObject(void* obj, v8::Persistent<v8::Objec t> wrapper)
866 {
867 ASSERT(MaybeDOMWrapper(wrapper));
868 #ifndef NDEBUG
869 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper);
870 switch (type) {
871 #define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: break;
872 ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE)
873 default: ASSERT(false);
874 #undef MAKE_CASE
875 }
876 #endif
877 getActiveDOMObjectMap().set(obj, wrapper);
878 }
879
880 // The caller must have increased node's ref count.
881 void V8Proxy::SetJSWrapperForDOMNode(Node* node, v8::Persistent<v8::Object> wrap per)
882 {
883 ASSERT(MaybeDOMWrapper(wrapper));
884 getDOMNodeMap().set(node, wrapper);
885 }
886
887 // Event listeners
888
889 static V8EventListener* FindEventListenerInList(V8EventListenerList& list,
890 v8::Local<v8::Value> listener,
891 bool isInline)
892 {
893 ASSERT(v8::Context::InContext());
894
895 if (!listener->IsObject())
896 return 0;
897
898 return list.find(listener->ToObject(), isInline);
899 }
900
901 // Find an existing wrapper for a JS event listener in the map.
902 PassRefPtr<V8EventListener> V8Proxy::FindV8EventListener(v8::Local<v8::Value> li stener,
903 bool isInline)
904 {
905 return FindEventListenerInList(m_event_listeners, listener, isInline);
906 }
907
908 PassRefPtr<V8EventListener> V8Proxy::FindOrCreateV8EventListener(v8::Local<v8::V alue> obj, bool isInline)
909 {
910 ASSERT(v8::Context::InContext());
911
912 if (!obj->IsObject())
913 return 0;
914
915 V8EventListener* wrapper =
916 FindEventListenerInList(m_event_listeners, obj, isInline);
917 if (wrapper)
918 return wrapper;
919
920 // Create a new one, and add to cache.
921 RefPtr<V8EventListener> new_listener =
922 V8EventListener::create(m_frame, v8::Local<v8::Object>::Cast(obj), isInline) ;
923 m_event_listeners.add(new_listener.get());
924
925 return new_listener;
926 }
927
928
929 // Object event listeners (such as XmlHttpRequest and MessagePort) are
930 // different from listeners on DOM nodes. An object event listener wrapper
931 // only holds a weak reference to the JS function. A strong reference can
932 // create a cycle.
933 //
934 // The lifetime of these objects is bounded by the life time of its JS
935 // wrapper. So we can create a hidden reference from the JS wrapper to
936 // to its JS function.
937 //
938 // (map)
939 // XHR <---------- JS_wrapper
940 // | (hidden) : ^
941 // V V : (may reachable by closure)
942 // V8_listener --------> JS_function
943 // (weak) <-- may create a cycle if it is strong
944 //
945 // The persistent reference is made weak in the constructor
946 // of V8ObjectEventListener.
947
948 PassRefPtr<V8EventListener> V8Proxy::FindObjectEventListener(
949 v8::Local<v8::Value> listener, bool isInline)
950 {
951 return FindEventListenerInList(m_xhr_listeners, listener, isInline);
952 }
953
954
955 PassRefPtr<V8EventListener> V8Proxy::FindOrCreateObjectEventListener(
956 v8::Local<v8::Value> obj, bool isInline)
957 {
958 ASSERT(v8::Context::InContext());
959
960 if (!obj->IsObject())
961 return 0;
962
963 V8EventListener* wrapper =
964 FindEventListenerInList(m_xhr_listeners, obj, isInline);
965 if (wrapper)
966 return wrapper;
967
968 // Create a new one, and add to cache.
969 RefPtr<V8EventListener> new_listener =
970 V8ObjectEventListener::create(m_frame, v8::Local<v8::Object>::Cast(obj), isI nline);
971 m_xhr_listeners.add(new_listener.get());
972
973 return new_listener.release();
974 }
975
976
977 static void RemoveEventListenerFromList(V8EventListenerList& list,
978 V8EventListener* listener)
979 {
980 list.remove(listener);
981 }
982
983
984 void V8Proxy::RemoveV8EventListener(V8EventListener* listener)
985 {
986 RemoveEventListenerFromList(m_event_listeners, listener);
987 }
988
989
990 void V8Proxy::RemoveObjectEventListener(V8ObjectEventListener* listener)
991 {
992 RemoveEventListenerFromList(m_xhr_listeners, listener);
993 }
994
995
996 static void DisconnectEventListenersInList(V8EventListenerList& list)
997 {
998 V8EventListenerList::iterator p = list.begin();
999 while (p != list.end()) {
1000 (*p)->disconnectFrame();
1001 ++p;
1002 }
1003 list.clear();
1004 }
1005
1006
1007 void V8Proxy::DisconnectEventListeners()
1008 {
1009 DisconnectEventListenersInList(m_event_listeners);
1010 DisconnectEventListenersInList(m_xhr_listeners);
1011 }
1012
1013
1014 v8::Handle<v8::Script> V8Proxy::CompileScript(v8::Handle<v8::String> code,
1015 const String& fileName,
1016 int baseLine)
1017 {
1018 const uint16_t* fileNameString = FromWebCoreString(fileName);
1019 v8::Handle<v8::String> name =
1020 v8::String::New(fileNameString, fileName.length());
1021 v8::Handle<v8::Integer> line = v8::Integer::New(baseLine);
1022 v8::ScriptOrigin origin(name, line);
1023 v8::Handle<v8::Script> script = v8::Script::Compile(code, &origin);
1024 return script;
1025 }
1026
1027 bool V8Proxy::HandleOutOfMemory()
1028 {
1029 v8::Local<v8::Context> context = v8::Context::GetCurrent();
1030
1031 if (!context->HasOutOfMemoryException())
1032 return false;
1033
1034 // Warning, error, disable JS for this frame?
1035 Frame* frame = V8Proxy::retrieveFrame(context);
1036
1037 V8Proxy* proxy = V8Proxy::retrieve(frame);
1038 if (proxy != NULL) {
1039 // Clean m_context, and event handlers.
1040 proxy->clearForClose();
1041 // Destroy the global object.
1042 proxy->DestroyGlobal();
1043 }
1044
1045 ChromiumBridge::notifyJSOutOfMemory(frame);
1046
1047 // Disable JS.
1048 Settings* settings = frame->settings();
1049 ASSERT(settings);
1050 settings->setJavaScriptEnabled(false);
1051
1052 return true;
1053 }
1054
1055 void V8Proxy::evaluateInNewWorld(const Vector<ScriptSourceCode>& sources)
1056 {
1057 InitContextIfNeeded();
1058 V8IsolatedWorld::evaluate(sources, this);
1059 }
1060
1061 void V8Proxy::evaluateInNewContext(const Vector<ScriptSourceCode>& sources)
1062 {
1063 InitContextIfNeeded();
1064
1065 v8::HandleScope handleScope;
1066
1067 // Set up the DOM window as the prototype of the new global object.
1068 v8::Handle<v8::Context> windowContext = m_context;
1069 v8::Handle<v8::Object> windowGlobal = windowContext->Global();
1070 v8::Handle<v8::Value> windowWrapper =
1071 V8Proxy::LookupDOMWrapper(V8ClassIndex::DOMWINDOW, windowGlobal);
1072
1073 ASSERT(V8Proxy::DOMWrapperToNative<DOMWindow>(windowWrapper) ==
1074 m_frame->domWindow());
1075
1076 v8::Persistent<v8::Context> context =
1077 createNewContext(v8::Handle<v8::Object>());
1078 v8::Context::Scope context_scope(context);
1079
1080 // Setup context id for JS debugger.
1081 v8::Handle<v8::Object> context_data = v8::Object::New();
1082 v8::Handle<v8::Value> window_context_data = windowContext->GetData();
1083 if (window_context_data->IsObject()) {
1084 v8::Handle<v8::String> property_name =
1085 v8::String::New(kContextDebugDataValue);
1086 context_data->Set(
1087 property_name,
1088 v8::Object::Cast(*window_context_data)->Get(property_name));
1089 }
1090 context_data->Set(v8::String::New(kContextDebugDataType),
1091 v8::String::New("injected"));
1092 context->SetData(context_data);
1093
1094 v8::Handle<v8::Object> global = context->Global();
1095
1096 v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__");
1097 global->Set(implicitProtoString, windowWrapper);
1098
1099 // Give the code running in the new context a way to get access to the
1100 // original context.
1101 global->Set(v8::String::New("contentWindow"), windowGlobal);
1102
1103 // Run code in the new context.
1104 for (size_t i = 0; i < sources.size(); ++i)
1105 evaluate(sources[i], 0);
1106
1107 // Using the default security token means that the canAccess is always
1108 // called, which is slow.
1109 // TODO(aa): Use tokens where possible. This will mean keeping track of all
1110 // created contexts so that they can all be updated when the document domain
1111 // changes.
1112 context->UseDefaultSecurityToken();
1113 context.Dispose();
1114 }
1115
1116 v8::Local<v8::Value> V8Proxy::evaluate(const ScriptSourceCode& source, Node* n)
1117 {
1118 ASSERT(v8::Context::InContext());
1119
1120 // Compile the script.
1121 v8::Local<v8::String> code = v8ExternalString(source.source());
1122 ChromiumBridge::traceEventBegin("v8.compile", n, "");
1123
1124 // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at
1125 // 1, whereas v8 starts at 0.
1126 v8::Handle<v8::Script> script = CompileScript(code, source.url(),
1127 source.startLine() - 1);
1128 ChromiumBridge::traceEventEnd("v8.compile", n, "");
1129
1130 ChromiumBridge::traceEventBegin("v8.run", n, "");
1131 v8::Local<v8::Value> result;
1132 {
1133 // Isolate exceptions that occur when executing the code. These
1134 // exceptions should not interfere with javascript code we might
1135 // evaluate from C++ when returning from here
1136 v8::TryCatch try_catch;
1137 try_catch.SetVerbose(true);
1138
1139 // Set inlineCode to true for <a href="javascript:doSomething()">
1140 // and false for <script>doSomething</script>. We make a rough guess at
1141 // this based on whether the script source has a URL.
1142 result = RunScript(script, source.url().string().isNull());
1143 }
1144 ChromiumBridge::traceEventEnd("v8.run", n, "");
1145 return result;
1146 }
1147
1148 v8::Local<v8::Value> V8Proxy::RunScript(v8::Handle<v8::Script> script,
1149 bool inline_code)
1150 {
1151 if (script.IsEmpty())
1152 return v8::Local<v8::Value>();
1153
1154 // Compute the source string and prevent against infinite recursion.
1155 if (m_recursion >= kMaxRecursionDepth) {
1156 v8::Local<v8::String> code =
1157 v8ExternalString("throw RangeError('Recursion too deep')");
1158 // TODO(kasperl): Ideally, we should be able to re-use the origin of the
1159 // script passed to us as the argument instead of using an empty string
1160 // and 0 baseLine.
1161 script = CompileScript(code, "", 0);
1162 }
1163
1164 if (HandleOutOfMemory())
1165 ASSERT(script.IsEmpty());
1166
1167 if (script.IsEmpty())
1168 return v8::Local<v8::Value>();
1169
1170 // Save the previous value of the inlineCode flag and update the flag for
1171 // the duration of the script invocation.
1172 bool previous_inline_code = inlineCode();
1173 setInlineCode(inline_code);
1174
1175 // Run the script and keep track of the current recursion depth.
1176 v8::Local<v8::Value> result;
1177 { ConsoleMessageScope scope;
1178 m_recursion++;
1179
1180 // Evaluating the JavaScript could cause the frame to be deallocated,
1181 // so we start the keep alive timer here.
1182 // Frame::keepAlive method adds the ref count of the frame and sets a
1183 // timer to decrease the ref count. It assumes that the current JavaScript
1184 // execution finishs before firing the timer.
1185 // See issue 1218756 and 914430.
1186 m_frame->keepAlive();
1187
1188 result = script->Run();
1189 m_recursion--;
1190 }
1191
1192 if (HandleOutOfMemory())
1193 ASSERT(result.IsEmpty());
1194
1195 // Handle V8 internal error situation (Out-of-memory).
1196 if (result.IsEmpty())
1197 return v8::Local<v8::Value>();
1198
1199 // Restore inlineCode flag.
1200 setInlineCode(previous_inline_code);
1201
1202 if (v8::V8::IsDead())
1203 HandleFatalErrorInV8();
1204
1205 return result;
1206 }
1207
1208
1209 v8::Local<v8::Value> V8Proxy::CallFunction(v8::Handle<v8::Function> function,
1210 v8::Handle<v8::Object> receiver,
1211 int argc,
1212 v8::Handle<v8::Value> args[])
1213 {
1214 // For now, we don't put any artificial limitations on the depth
1215 // of recursion that stems from calling functions. This is in
1216 // contrast to the script evaluations.
1217 v8::Local<v8::Value> result;
1218 {
1219 ConsoleMessageScope scope;
1220
1221 // Evaluating the JavaScript could cause the frame to be deallocated,
1222 // so we start the keep alive timer here.
1223 // Frame::keepAlive method adds the ref count of the frame and sets a
1224 // timer to decrease the ref count. It assumes that the current JavaScript
1225 // execution finishs before firing the timer.
1226 // See issue 1218756 and 914430.
1227 m_frame->keepAlive();
1228
1229 result = function->Call(receiver, argc, args);
1230 }
1231
1232 if (v8::V8::IsDead())
1233 HandleFatalErrorInV8();
1234
1235 return result;
1236 }
1237
1238
1239 v8::Local<v8::Value> V8Proxy::NewInstance(v8::Handle<v8::Function> constructor,
1240 int argc,
1241 v8::Handle<v8::Value> args[])
1242 {
1243 // No artificial limitations on the depth of recursion, see comment in
1244 // V8Proxy::CallFunction.
1245 v8::Local<v8::Value> result;
1246 {
1247 ConsoleMessageScope scope;
1248
1249 // See comment in V8Proxy::CallFunction.
1250 m_frame->keepAlive();
1251
1252 result = constructor->NewInstance(argc, args);
1253 }
1254
1255 if (v8::V8::IsDead())
1256 HandleFatalErrorInV8();
1257
1258 return result;
1259 }
1260
1261
1262 v8::Local<v8::Function> V8Proxy::GetConstructor(V8ClassIndex::V8WrapperType t){
1263 // A DOM constructor is a function instance created from a DOM constructor
1264 // template. There is one instance per context. A DOM constructor is
1265 // different from a normal function in two ways:
1266 // 1) it cannot be called as constructor (aka, used to create a DOM object)
1267 // 2) its __proto__ points to Object.prototype rather than
1268 // Function.prototype.
1269 // The reason for 2) is that, in Safari, a DOM constructor is a normal JS
1270 // object, but not a function. Hotmail relies on the fact that, in Safari,
1271 // HTMLElement.__proto__ == Object.prototype.
1272 //
1273 // m_object_prototype is a cache of the original Object.prototype.
1274
1275 ASSERT(ContextInitialized());
1276 // Enter the context of the proxy to make sure that the
1277 // function is constructed in the context corresponding to
1278 // this proxy.
1279 v8::Context::Scope scope(m_context);
1280 v8::Handle<v8::FunctionTemplate> templ = GetTemplate(t);
1281 // Getting the function might fail if we're running out of
1282 // stack or memory.
1283 v8::TryCatch try_catch;
1284 v8::Local<v8::Function> value = templ->GetFunction();
1285 if (value.IsEmpty())
1286 return v8::Local<v8::Function>();
1287 // Hotmail fix, see comments above.
1288 value->Set(v8::String::New("__proto__"), m_object_prototype);
1289 return value;
1290 }
1291
1292
1293 v8::Local<v8::Object> V8Proxy::CreateWrapperFromCache(V8ClassIndex::V8WrapperTyp e type) {
1294 int class_index = V8ClassIndex::ToInt(type);
1295 v8::Local<v8::Value> cached_object =
1296 m_wrapper_boilerplates->Get(v8::Integer::New(class_index));
1297 if (cached_object->IsObject()) {
1298 v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(cached_object);
1299 return object->Clone();
1300 }
1301
1302 // Not in cache.
1303 InitContextIfNeeded();
1304 v8::Context::Scope scope(m_context);
1305 v8::Local<v8::Function> function = GetConstructor(type);
1306 v8::Local<v8::Object> instance = SafeAllocation::NewInstance(function);
1307 if (!instance.IsEmpty()) {
1308 m_wrapper_boilerplates->Set(v8::Integer::New(class_index), instance);
1309 return instance->Clone();
1310 }
1311 return v8::Local<v8::Object>();
1312 }
1313
1314
1315 // Get the string 'toString'.
1316 static v8::Persistent<v8::String> GetToStringName() {
1317 static v8::Persistent<v8::String> value;
1318 if (value.IsEmpty())
1319 value = v8::Persistent<v8::String>::New(v8::String::New("toString"));
1320 return value;
1321 }
1322
1323
1324 static v8::Handle<v8::Value> ConstructorToString(const v8::Arguments& args) {
1325 // The DOM constructors' toString functions grab the current toString
1326 // for Functions by taking the toString function of itself and then
1327 // calling it with the constructor as its receiver. This means that
1328 // changes to the Function prototype chain or toString function are
1329 // reflected when printing DOM constructors. The only wart is that
1330 // changes to a DOM constructor's toString's toString will cause the
1331 // toString of the DOM constructor itself to change. This is extremely
1332 // obscure and unlikely to be a problem.
1333 v8::Handle<v8::Value> val = args.Callee()->Get(GetToStringName());
1334 if (!val->IsFunction()) return v8::String::New("");
1335 return v8::Handle<v8::Function>::Cast(val)->Call(args.This(), 0, NULL);
1336 }
1337
1338
1339 v8::Persistent<v8::FunctionTemplate> V8Proxy::GetTemplate(
1340 V8ClassIndex::V8WrapperType type)
1341 {
1342 v8::Persistent<v8::FunctionTemplate>* cache_cell =
1343 V8ClassIndex::GetCache(type);
1344 if (!(*cache_cell).IsEmpty())
1345 return *cache_cell;
1346
1347 // not found
1348 FunctionTemplateFactory factory = V8ClassIndex::GetFactory(type);
1349 v8::Persistent<v8::FunctionTemplate> desc = factory();
1350 // DOM constructors are functions and should print themselves as such.
1351 // However, we will later replace their prototypes with Object
1352 // prototypes so we need to explicitly override toString on the
1353 // instance itself. If we later make DOM constructors full objects
1354 // we can give them class names instead and Object.prototype.toString
1355 // will work so we can remove this code.
1356 static v8::Persistent<v8::FunctionTemplate> to_string_template;
1357 if (to_string_template.IsEmpty()) {
1358 to_string_template = v8::Persistent<v8::FunctionTemplate>::New(
1359 v8::FunctionTemplate::New(ConstructorToString));
1360 }
1361 desc->Set(GetToStringName(), to_string_template);
1362 switch (type) {
1363 case V8ClassIndex::CSSSTYLEDECLARATION:
1364 // The named property handler for style declarations has a
1365 // setter. Therefore, the interceptor has to be on the object
1366 // itself and not on the prototype object.
1367 desc->InstanceTemplate()->SetNamedPropertyHandler(
1368 USE_NAMED_PROPERTY_GETTER(CSSStyleDeclaration),
1369 USE_NAMED_PROPERTY_SETTER(CSSStyleDeclaration));
1370 setCollectionStringOrNullIndexedGetter<CSSStyleDeclaration>(desc);
1371 break;
1372 case V8ClassIndex::CSSRULELIST:
1373 setCollectionIndexedGetter<CSSRuleList, CSSRule>(desc,
1374 V8ClassIndex::CSSRULE);
1375 break;
1376 case V8ClassIndex::CSSVALUELIST:
1377 setCollectionIndexedGetter<CSSValueList, CSSValue>(
1378 desc,
1379 V8ClassIndex::CSSVALUE);
1380 break;
1381 case V8ClassIndex::CSSVARIABLESDECLARATION:
1382 setCollectionStringOrNullIndexedGetter<CSSVariablesDeclaration>(desc);
1383 break;
1384 case V8ClassIndex::WEBKITCSSTRANSFORMVALUE:
1385 setCollectionIndexedGetter<WebKitCSSTransformValue, CSSValue>(
1386 desc,
1387 V8ClassIndex::CSSVALUE);
1388 break;
1389 case V8ClassIndex::UNDETECTABLEHTMLCOLLECTION:
1390 desc->InstanceTemplate()->MarkAsUndetectable(); // fall through
1391 case V8ClassIndex::HTMLCOLLECTION:
1392 desc->InstanceTemplate()->SetNamedPropertyHandler(
1393 USE_NAMED_PROPERTY_GETTER(HTMLCollection));
1394 desc->InstanceTemplate()->SetCallAsFunctionHandler(
1395 USE_CALLBACK(HTMLCollectionCallAsFunction));
1396 setCollectionIndexedGetter<HTMLCollection, Node>(desc,
1397 V8ClassIndex::NODE);
1398 break;
1399 case V8ClassIndex::HTMLOPTIONSCOLLECTION:
1400 desc->InstanceTemplate()->SetNamedPropertyHandler(
1401 USE_NAMED_PROPERTY_GETTER(HTMLCollection));
1402 desc->InstanceTemplate()->SetIndexedPropertyHandler(
1403 USE_INDEXED_PROPERTY_GETTER(HTMLOptionsCollection),
1404 USE_INDEXED_PROPERTY_SETTER(HTMLOptionsCollection));
1405 desc->InstanceTemplate()->SetCallAsFunctionHandler(
1406 USE_CALLBACK(HTMLCollectionCallAsFunction));
1407 break;
1408 case V8ClassIndex::HTMLSELECTELEMENT:
1409 desc->InstanceTemplate()->SetNamedPropertyHandler(
1410 USE_NAMED_PROPERTY_GETTER(HTMLSelectElementCollection));
1411 desc->InstanceTemplate()->SetIndexedPropertyHandler(
1412 nodeCollectionIndexedPropertyGetter<HTMLSelectElement>,
1413 USE_INDEXED_PROPERTY_SETTER(HTMLSelectElementCollection),
1414 0,
1415 0,
1416 nodeCollectionIndexedPropertyEnumerator<HTMLSelectElement>,
1417 v8::Integer::New(V8ClassIndex::NODE));
1418 break;
1419 case V8ClassIndex::HTMLDOCUMENT: {
1420 desc->InstanceTemplate()->SetNamedPropertyHandler(
1421 USE_NAMED_PROPERTY_GETTER(HTMLDocument),
1422 0,
1423 0,
1424 USE_NAMED_PROPERTY_DELETER(HTMLDocument));
1425
1426 // We add an extra internal field to all Document wrappers for
1427 // storing a per document DOMImplementation wrapper.
1428 //
1429 // Additionally, we add two extra internal fields for
1430 // HTMLDocuments to implement temporary shadowing of
1431 // document.all. One field holds an object that is used as a
1432 // marker. The other field holds the marker object if
1433 // document.all is not shadowed and some other value if
1434 // document.all is shadowed.
1435 v8::Local<v8::ObjectTemplate> instance_template =
1436 desc->InstanceTemplate();
1437 ASSERT(instance_template->InternalFieldCount() ==
1438 V8Custom::kDefaultWrapperInternalFieldCount);
1439 instance_template->SetInternalFieldCount(
1440 V8Custom::kHTMLDocumentInternalFieldCount);
1441 break;
1442 }
1443 #if ENABLE(SVG)
1444 case V8ClassIndex::SVGDOCUMENT: // fall through
1445 #endif
1446 case V8ClassIndex::DOCUMENT: {
1447 // We add an extra internal field to all Document wrappers for
1448 // storing a per document DOMImplementation wrapper.
1449 v8::Local<v8::ObjectTemplate> instance_template =
1450 desc->InstanceTemplate();
1451 ASSERT(instance_template->InternalFieldCount() ==
1452 V8Custom::kDefaultWrapperInternalFieldCount);
1453 instance_template->SetInternalFieldCount(
1454 V8Custom::kDocumentMinimumInternalFieldCount);
1455 break;
1456 }
1457 case V8ClassIndex::HTMLAPPLETELEMENT: // fall through
1458 case V8ClassIndex::HTMLEMBEDELEMENT: // fall through
1459 case V8ClassIndex::HTMLOBJECTELEMENT:
1460 // HTMLAppletElement, HTMLEmbedElement and HTMLObjectElement are
1461 // inherited from HTMLPlugInElement, and they share the same property
1462 // handling code.
1463 desc->InstanceTemplate()->SetNamedPropertyHandler(
1464 USE_NAMED_PROPERTY_GETTER(HTMLPlugInElement),
1465 USE_NAMED_PROPERTY_SETTER(HTMLPlugInElement));
1466 desc->InstanceTemplate()->SetIndexedPropertyHandler(
1467 USE_INDEXED_PROPERTY_GETTER(HTMLPlugInElement),
1468 USE_INDEXED_PROPERTY_SETTER(HTMLPlugInElement));
1469 desc->InstanceTemplate()->SetCallAsFunctionHandler(
1470 USE_CALLBACK(HTMLPlugInElement));
1471 break;
1472 case V8ClassIndex::HTMLFRAMESETELEMENT:
1473 desc->InstanceTemplate()->SetNamedPropertyHandler(
1474 USE_NAMED_PROPERTY_GETTER(HTMLFrameSetElement));
1475 break;
1476 case V8ClassIndex::HTMLFORMELEMENT:
1477 desc->InstanceTemplate()->SetNamedPropertyHandler(
1478 USE_NAMED_PROPERTY_GETTER(HTMLFormElement));
1479 desc->InstanceTemplate()->SetIndexedPropertyHandler(
1480 USE_INDEXED_PROPERTY_GETTER(HTMLFormElement),
1481 0,
1482 0,
1483 0,
1484 nodeCollectionIndexedPropertyEnumerator<HTMLFormElement>,
1485 v8::Integer::New(V8ClassIndex::NODE));
1486 break;
1487 case V8ClassIndex::CANVASPIXELARRAY:
1488 desc->InstanceTemplate()->SetIndexedPropertyHandler(
1489 USE_INDEXED_PROPERTY_GETTER(CanvasPixelArray),
1490 USE_INDEXED_PROPERTY_SETTER(CanvasPixelArray));
1491 break;
1492 case V8ClassIndex::STYLESHEET: // fall through
1493 case V8ClassIndex::CSSSTYLESHEET: {
1494 // We add an extra internal field to hold a reference to
1495 // the owner node.
1496 v8::Local<v8::ObjectTemplate> instance_template =
1497 desc->InstanceTemplate();
1498 ASSERT(instance_template->InternalFieldCount() ==
1499 V8Custom::kDefaultWrapperInternalFieldCount);
1500 instance_template->SetInternalFieldCount(
1501 V8Custom::kStyleSheetInternalFieldCount);
1502 break;
1503 }
1504 case V8ClassIndex::MEDIALIST:
1505 setCollectionStringOrNullIndexedGetter<MediaList>(desc);
1506 break;
1507 case V8ClassIndex::MIMETYPEARRAY:
1508 setCollectionIndexedAndNamedGetters<MimeTypeArray, MimeType>(
1509 desc,
1510 V8ClassIndex::MIMETYPE);
1511 break;
1512 case V8ClassIndex::NAMEDNODEMAP:
1513 desc->InstanceTemplate()->SetNamedPropertyHandler(
1514 USE_NAMED_PROPERTY_GETTER(NamedNodeMap));
1515 desc->InstanceTemplate()->SetIndexedPropertyHandler(
1516 USE_INDEXED_PROPERTY_GETTER(NamedNodeMap),
1517 0,
1518 0,
1519 0,
1520 collectionIndexedPropertyEnumerator<NamedNodeMap>,
1521 v8::Integer::New(V8ClassIndex::NODE));
1522 break;
1523 #if ENABLE(DOM_STORAGE)
1524 case V8ClassIndex::STORAGE:
1525 desc->InstanceTemplate()->SetNamedPropertyHandler(
1526 USE_NAMED_PROPERTY_GETTER(Storage),
1527 USE_NAMED_PROPERTY_SETTER(Storage),
1528 0,
1529 USE_NAMED_PROPERTY_DELETER(Storage),
1530 V8Custom::v8StorageNamedPropertyEnumerator);
1531 desc->InstanceTemplate()->SetIndexedPropertyHandler(
1532 USE_INDEXED_PROPERTY_GETTER(Storage),
1533 USE_INDEXED_PROPERTY_SETTER(Storage),
1534 0,
1535 USE_INDEXED_PROPERTY_DELETER(Storage));
1536 break;
1537 #endif
1538 case V8ClassIndex::NODELIST:
1539 setCollectionIndexedGetter<NodeList, Node>(desc, V8ClassIndex::NODE);
1540 desc->InstanceTemplate()->SetNamedPropertyHandler(
1541 USE_NAMED_PROPERTY_GETTER(NodeList));
1542 break;
1543 case V8ClassIndex::PLUGIN:
1544 setCollectionIndexedAndNamedGetters<Plugin, MimeType>(
1545 desc,
1546 V8ClassIndex::MIMETYPE);
1547 break;
1548 case V8ClassIndex::PLUGINARRAY:
1549 setCollectionIndexedAndNamedGetters<PluginArray, Plugin>(
1550 desc,
1551 V8ClassIndex::PLUGIN);
1552 break;
1553 case V8ClassIndex::STYLESHEETLIST:
1554 desc->InstanceTemplate()->SetNamedPropertyHandler(
1555 USE_NAMED_PROPERTY_GETTER(StyleSheetList));
1556 setCollectionIndexedGetter<StyleSheetList, StyleSheet>(
1557 desc,
1558 V8ClassIndex::STYLESHEET);
1559 break;
1560 case V8ClassIndex::DOMWINDOW: {
1561 v8::Local<v8::Signature> default_signature = v8::Signature::New(desc);
1562
1563 desc->PrototypeTemplate()->SetNamedPropertyHandler(
1564 USE_NAMED_PROPERTY_GETTER(DOMWindow));
1565 desc->PrototypeTemplate()->SetIndexedPropertyHandler(
1566 USE_INDEXED_PROPERTY_GETTER(DOMWindow));
1567
1568 desc->SetHiddenPrototype(true);
1569
1570 // Reserve spaces for references to location, history and
1571 // navigator objects.
1572 v8::Local<v8::ObjectTemplate> instance_template =
1573 desc->InstanceTemplate();
1574 instance_template->SetInternalFieldCount(
1575 V8Custom::kDOMWindowInternalFieldCount);
1576
1577 // Set access check callbacks, but turned off initially.
1578 // When a context is detached from a frame, turn on the access check.
1579 // Turning on checks also invalidates inline caches of the object.
1580 instance_template->SetAccessCheckCallbacks(
1581 V8Custom::v8DOMWindowNamedSecurityCheck,
1582 V8Custom::v8DOMWindowIndexedSecurityCheck,
1583 v8::Integer::New(V8ClassIndex::DOMWINDOW),
1584 false);
1585 break;
1586 }
1587 case V8ClassIndex::LOCATION: {
1588 // For security reasons, these functions are on the instance
1589 // instead of on the prototype object to insure that they cannot
1590 // be overwritten.
1591 v8::Local<v8::ObjectTemplate> instance = desc->InstanceTemplate();
1592 instance->SetAccessor(
1593 v8::String::New("reload"),
1594 V8Custom::v8LocationReloadAccessorGetter,
1595 0,
1596 v8::Handle<v8::Value>(),
1597 v8::ALL_CAN_READ,
1598 static_cast<v8::PropertyAttribute>(v8::DontDelete|v8::ReadOnly));
1599
1600 instance->SetAccessor(
1601 v8::String::New("replace"),
1602 V8Custom::v8LocationReplaceAccessorGetter,
1603 0,
1604 v8::Handle<v8::Value>(),
1605 v8::ALL_CAN_READ,
1606 static_cast<v8::PropertyAttribute>(v8::DontDelete|v8::ReadOnly));
1607
1608 instance->SetAccessor(
1609 v8::String::New("assign"),
1610 V8Custom::v8LocationAssignAccessorGetter,
1611 0,
1612 v8::Handle<v8::Value>(),
1613 v8::ALL_CAN_READ,
1614 static_cast<v8::PropertyAttribute>(v8::DontDelete|v8::ReadOnly));
1615 break;
1616 }
1617 case V8ClassIndex::HISTORY: {
1618 break;
1619 }
1620
1621 case V8ClassIndex::MESSAGECHANNEL: {
1622 // Reserve two more internal fields for referencing the port1
1623 // and port2 wrappers. This ensures that the port wrappers are
1624 // kept alive when the channel wrapper is.
1625 desc->SetCallHandler(USE_CALLBACK(MessageChannelConstructor));
1626 v8::Local<v8::ObjectTemplate> instance_template =
1627 desc->InstanceTemplate();
1628 instance_template->SetInternalFieldCount(
1629 V8Custom::kMessageChannelInternalFieldCount);
1630 break;
1631 }
1632
1633 case V8ClassIndex::MESSAGEPORT: {
1634 // Reserve one more internal field for keeping event listeners.
1635 v8::Local<v8::ObjectTemplate> instance_template =
1636 desc->InstanceTemplate();
1637 instance_template->SetInternalFieldCount(
1638 V8Custom::kMessagePortInternalFieldCount);
1639 break;
1640 }
1641
1642 #if ENABLE(WORKERS)
1643 case V8ClassIndex::WORKER: {
1644 // Reserve one more internal field for keeping event listeners.
1645 v8::Local<v8::ObjectTemplate> instance_template =
1646 desc->InstanceTemplate();
1647 instance_template->SetInternalFieldCount(
1648 V8Custom::kWorkerInternalFieldCount);
1649 desc->SetCallHandler(USE_CALLBACK(WorkerConstructor));
1650 break;
1651 }
1652
1653 case V8ClassIndex::WORKERCONTEXT: {
1654 // Reserve one more internal field for keeping event listeners.
1655 v8::Local<v8::ObjectTemplate> instance_template =
1656 desc->InstanceTemplate();
1657 instance_template->SetInternalFieldCount(
1658 V8Custom::kWorkerContextInternalFieldCount);
1659 break;
1660 }
1661 #endif // WORKERS
1662
1663
1664 // The following objects are created from JavaScript.
1665 case V8ClassIndex::DOMPARSER:
1666 desc->SetCallHandler(USE_CALLBACK(DOMParserConstructor));
1667 break;
1668 #if ENABLE(VIDEO)
1669 case V8ClassIndex::HTMLAUDIOELEMENT:
1670 desc->SetCallHandler(USE_CALLBACK(HTMLAudioElementConstructor));
1671 break;
1672 #endif
1673 case V8ClassIndex::HTMLIMAGEELEMENT:
1674 desc->SetCallHandler(USE_CALLBACK(HTMLImageElementConstructor));
1675 break;
1676 case V8ClassIndex::HTMLOPTIONELEMENT:
1677 desc->SetCallHandler(USE_CALLBACK(HTMLOptionElementConstructor));
1678 break;
1679 case V8ClassIndex::WEBKITCSSMATRIX:
1680 desc->SetCallHandler(USE_CALLBACK(WebKitCSSMatrixConstructor));
1681 break;
1682 case V8ClassIndex::WEBKITPOINT:
1683 desc->SetCallHandler(USE_CALLBACK(WebKitPointConstructor));
1684 break;
1685 case V8ClassIndex::XMLSERIALIZER:
1686 desc->SetCallHandler(USE_CALLBACK(XMLSerializerConstructor));
1687 break;
1688 case V8ClassIndex::XMLHTTPREQUEST: {
1689 // Reserve one more internal field for keeping event listeners.
1690 v8::Local<v8::ObjectTemplate> instance_template =
1691 desc->InstanceTemplate();
1692 instance_template->SetInternalFieldCount(
1693 V8Custom::kXMLHttpRequestInternalFieldCount);
1694 desc->SetCallHandler(USE_CALLBACK(XMLHttpRequestConstructor));
1695 break;
1696 }
1697 case V8ClassIndex::XMLHTTPREQUESTUPLOAD: {
1698 // Reserve one more internal field for keeping event listeners.
1699 v8::Local<v8::ObjectTemplate> instance_template =
1700 desc->InstanceTemplate();
1701 instance_template->SetInternalFieldCount(
1702 V8Custom::kXMLHttpRequestInternalFieldCount);
1703 break;
1704 }
1705 case V8ClassIndex::XPATHEVALUATOR:
1706 desc->SetCallHandler(USE_CALLBACK(XPathEvaluatorConstructor));
1707 break;
1708 case V8ClassIndex::XSLTPROCESSOR:
1709 desc->SetCallHandler(USE_CALLBACK(XSLTProcessorConstructor));
1710 break;
1711 case V8ClassIndex::CLIENTRECTLIST:
1712 desc->InstanceTemplate()->SetIndexedPropertyHandler(
1713 USE_INDEXED_PROPERTY_GETTER(ClientRectList));
1714 break;
1715 default:
1716 break;
1717 }
1718
1719 *cache_cell = desc;
1720 return desc;
1721 }
1722
1723
1724 bool V8Proxy::ContextInitialized()
1725 {
1726 // m_context, m_global, m_object_prototype and m_wrapper_boilerplates should
1727 // all be non-empty if if m_context is non-empty.
1728 ASSERT(m_context.IsEmpty() || !m_global.IsEmpty());
1729 ASSERT(m_context.IsEmpty() || !m_object_prototype.IsEmpty());
1730 ASSERT(m_context.IsEmpty() || !m_wrapper_boilerplates.IsEmpty());
1731 return !m_context.IsEmpty();
1732 }
1733
1734
1735 DOMWindow* V8Proxy::retrieveWindow()
1736 {
1737 // TODO: This seems very fragile. How do we know that the global object
1738 // from the current context is something sensible? Do we need to use the
1739 // last entered here? Who calls this?
1740 return retrieveWindow(v8::Context::GetCurrent());
1741 }
1742
1743
1744 DOMWindow* V8Proxy::retrieveWindow(v8::Handle<v8::Context> context)
1745 {
1746 v8::Handle<v8::Object> global = context->Global();
1747 ASSERT(!global.IsEmpty());
1748 global = LookupDOMWrapper(V8ClassIndex::DOMWINDOW, global);
1749 ASSERT(!global.IsEmpty());
1750 return ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, global);
1751 }
1752
1753
1754 Frame* V8Proxy::retrieveFrame(v8::Handle<v8::Context> context)
1755 {
1756 return retrieveWindow(context)->frame();
1757 }
1758
1759
1760 Frame* V8Proxy::retrieveFrameForEnteredContext()
1761 {
1762 v8::Handle<v8::Context> context = v8::Context::GetEntered();
1763 if (context.IsEmpty())
1764 return 0;
1765 return retrieveFrame(context);
1766 }
1767
1768
1769 Frame* V8Proxy::retrieveFrameForCurrentContext()
1770 {
1771 v8::Handle<v8::Context> context = v8::Context::GetCurrent();
1772 if (context.IsEmpty())
1773 return 0;
1774 return retrieveFrame(context);
1775 }
1776
1777
1778 Frame* V8Proxy::retrieveFrameForCallingContext()
1779 {
1780 v8::Handle<v8::Context> context = v8::Context::GetCalling();
1781 if (context.IsEmpty())
1782 return 0;
1783 return retrieveFrame(context);
1784 }
1785
1786
1787 Frame* V8Proxy::retrieveFrame()
1788 {
1789 DOMWindow* window = retrieveWindow();
1790 return window ? window->frame() : 0;
1791 }
1792
1793
1794 V8Proxy* V8Proxy::retrieve()
1795 {
1796 DOMWindow* window = retrieveWindow();
1797 ASSERT(window);
1798 return retrieve(window->frame());
1799 }
1800
1801 V8Proxy* V8Proxy::retrieve(Frame* frame)
1802 {
1803 if (!frame)
1804 return 0;
1805 return frame->script()->isEnabled() ? frame->script()->proxy() : 0;
1806 }
1807
1808
1809 V8Proxy* V8Proxy::retrieve(ScriptExecutionContext* context)
1810 {
1811 if (!context->isDocument())
1812 return 0;
1813 return retrieve(static_cast<Document*>(context)->frame());
1814 }
1815
1816
1817 void V8Proxy::disconnectFrame()
1818 {
1819 // disconnect all event listeners
1820 DisconnectEventListeners();
1821 }
1822
1823
1824 bool V8Proxy::isEnabled()
1825 {
1826 Settings* settings = m_frame->settings();
1827 if (!settings)
1828 return false;
1829
1830 // In the common case, JavaScript is enabled and we're done.
1831 if (settings->isJavaScriptEnabled())
1832 return true;
1833
1834 // If JavaScript has been disabled, we need to look at the frame to tell
1835 // whether this script came from the web or the embedder. Scripts from the
1836 // embedder are safe to run, but scripts from the other sources are
1837 // disallowed.
1838 Document* document = m_frame->document();
1839 if (!document)
1840 return false;
1841
1842 SecurityOrigin* origin = document->securityOrigin();
1843 if (origin->protocol().isEmpty())
1844 return false; // Uninitialized document
1845
1846 if (origin->protocol() == "http" || origin->protocol() == "https")
1847 return false; // Web site
1848
1849 // TODO(darin): the following are application decisions, and they should
1850 // not be made at this layer. instead, we should bridge out to the
1851 // embedder to allow them to override policy here.
1852
1853 if (origin->protocol() == ChromiumBridge::uiResourceProtocol())
1854 return true; // Embedder's scripts are ok to run
1855
1856 // If the scheme is ftp: or file:, an empty file name indicates a directory
1857 // listing, which requires JavaScript to function properly.
1858 const char* kDirProtocols[] = { "ftp", "file" };
1859 for (size_t i = 0; i < arraysize(kDirProtocols); ++i) {
1860 if (origin->protocol() == kDirProtocols[i]) {
1861 const KURL& url = document->url();
1862 return url.pathAfterLastSlash() == url.pathEnd();
1863 }
1864 }
1865
1866 return false; // Other protocols fall through to here
1867 }
1868
1869
1870 void V8Proxy::UpdateDocumentWrapper(v8::Handle<v8::Value> wrapper) {
1871 ClearDocumentWrapper();
1872
1873 ASSERT(m_document.IsEmpty());
1874 m_document = v8::Persistent<v8::Value>::New(wrapper);
1875 #ifndef NDEBUG
1876 RegisterGlobalHandle(PROXY, this, m_document);
1877 #endif
1878 }
1879
1880
1881 void V8Proxy::ClearDocumentWrapper()
1882 {
1883 if (!m_document.IsEmpty()) {
1884 #ifndef NDEBUG
1885 UnregisterGlobalHandle(this, m_document);
1886 #endif
1887 m_document.Dispose();
1888 m_document.Clear();
1889 }
1890 }
1891
1892
1893 void V8Proxy::UpdateDocumentWrapperCache()
1894 {
1895 v8::HandleScope handle_scope;
1896 v8::Context::Scope context_scope(GetContext());
1897
1898 // If the document has no frame, NodeToV8Object might get the
1899 // document wrapper for a document that is about to be deleted.
1900 // If the ForceSet below causes a garbage collection, the document
1901 // might get deleted and the global handle for the document
1902 // wrapper cleared. Using the cleared global handle will lead to
1903 // crashes. In this case we clear the cache and let the DOMWindow
1904 // accessor handle access to the document.
1905 if (!m_frame->document()->frame()) {
1906 ClearDocumentWrapperCache();
1907 return;
1908 }
1909
1910 v8::Handle<v8::Value> document_wrapper = NodeToV8Object(m_frame->document()) ;
1911
1912 // If instantiation of the document wrapper fails, clear the cache
1913 // and let the DOMWindow accessor handle access to the document.
1914 if (document_wrapper.IsEmpty()) {
1915 ClearDocumentWrapperCache();
1916 return;
1917 }
1918
1919 m_context->Global()->ForceSet(v8::String::New("document"),
1920 document_wrapper,
1921 static_cast<v8::PropertyAttribute>(v8::ReadOnl y | v8::DontDelete));
1922 }
1923
1924
1925 void V8Proxy::ClearDocumentWrapperCache()
1926 {
1927 ASSERT(!m_context.IsEmpty());
1928 m_context->Global()->ForceDelete(v8::String::New("document"));
1929 }
1930
1931
1932 void V8Proxy::DisposeContextHandles() {
1933 if (!m_context.IsEmpty()) {
1934 m_context.Dispose();
1935 m_context.Clear();
1936 }
1937
1938 if (!m_wrapper_boilerplates.IsEmpty()) {
1939 #ifndef NDEBUG
1940 UnregisterGlobalHandle(this, m_wrapper_boilerplates);
1941 #endif
1942 m_wrapper_boilerplates.Dispose();
1943 m_wrapper_boilerplates.Clear();
1944 }
1945
1946 if (!m_object_prototype.IsEmpty()) {
1947 #ifndef NDEBUG
1948 UnregisterGlobalHandle(this, m_object_prototype);
1949 #endif
1950 m_object_prototype.Dispose();
1951 m_object_prototype.Clear();
1952 }
1953 }
1954
1955 void V8Proxy::clearForClose()
1956 {
1957 if (!m_context.IsEmpty()) {
1958 v8::HandleScope handle_scope;
1959
1960 ClearDocumentWrapper();
1961 DisposeContextHandles();
1962 }
1963 }
1964
1965
1966 void V8Proxy::clearForNavigation()
1967 {
1968 // disconnect all event listeners
1969 DisconnectEventListeners();
1970
1971 if (!m_context.IsEmpty()) {
1972 v8::HandleScope handle;
1973 ClearDocumentWrapper();
1974
1975 v8::Context::Scope context_scope(m_context);
1976
1977 // Clear the document wrapper cache before turning on access checks on
1978 // the old DOMWindow wrapper. This way, access to the document wrapper
1979 // will be protected by the security checks on the DOMWindow wrapper.
1980 ClearDocumentWrapperCache();
1981
1982 // Turn on access check on the old DOMWindow wrapper.
1983 v8::Handle<v8::Object> wrapper =
1984 LookupDOMWrapper(V8ClassIndex::DOMWINDOW, m_global);
1985 ASSERT(!wrapper.IsEmpty());
1986 wrapper->TurnOnAccessCheck();
1987
1988 // Separate the context from its global object.
1989 m_context->DetachGlobal();
1990
1991 DisposeContextHandles();
1992
1993 // Reinitialize the context so the global object points to
1994 // the new DOM window.
1995 InitContextIfNeeded();
1996 }
1997 }
1998
1999
2000 void V8Proxy::SetSecurityToken() {
2001 Document* document = m_frame->document();
2002 // Setup security origin and security token
2003 if (!document) {
2004 m_context->UseDefaultSecurityToken();
2005 return;
2006 }
2007
2008 // Ask the document's SecurityOrigin to generate a security token.
2009 // If two tokens are equal, then the SecurityOrigins canAccess each other.
2010 // If two tokens are not equal, then we have to call canAccess.
2011 // Note: we can't use the HTTPOrigin if it was set from the DOM.
2012 SecurityOrigin* origin = document->securityOrigin();
2013 String token;
2014 if (!origin->domainWasSetInDOM())
2015 token = document->securityOrigin()->toString();
2016
2017 // An empty or "null" token means we always have to call
2018 // canAccess. The toString method on securityOrigins returns the
2019 // string "null" for empty security origins and for security
2020 // origins that should only allow access to themselves. In this
2021 // case, we use the global object as the security token to avoid
2022 // calling canAccess when a script accesses its own objects.
2023 if (token.isEmpty() || token == "null") {
2024 m_context->UseDefaultSecurityToken();
2025 return;
2026 }
2027
2028 CString utf8_token = token.utf8();
2029 // NOTE: V8 does identity comparison in fast path, must use a symbol
2030 // as the security token.
2031 m_context->SetSecurityToken(
2032 v8::String::NewSymbol(utf8_token.data(), utf8_token.length()));
2033 }
2034
2035
2036 void V8Proxy::updateDocument()
2037 {
2038 if (!m_frame->document())
2039 return;
2040
2041 if (m_global.IsEmpty()) {
2042 ASSERT(m_context.IsEmpty());
2043 return;
2044 }
2045
2046 // We have a new document and we need to update the cache.
2047 UpdateDocumentWrapperCache();
2048
2049 updateSecurityOrigin();
2050 }
2051
2052 void V8Proxy::updateSecurityOrigin()
2053 {
2054 v8::HandleScope scope;
2055 SetSecurityToken();
2056 }
2057
2058 // Same origin policy implementation:
2059 //
2060 // Same origin policy prevents JS code from domain A access JS & DOM objects
2061 // in a different domain B. There are exceptions and several objects are
2062 // accessible by cross-domain code. For example, the window.frames object is
2063 // accessible by code from a different domain, but window.document is not.
2064 //
2065 // The binding code sets security check callbacks on a function template,
2066 // and accessing instances of the template calls the callback function.
2067 // The callback function checks same origin policy.
2068 //
2069 // Callback functions are expensive. V8 uses a security token string to do
2070 // fast access checks for the common case where source and target are in the
2071 // same domain. A security token is a string object that represents
2072 // the protocol/url/port of a domain.
2073 //
2074 // There are special cases where a security token matching is not enough.
2075 // For example, JavaScript can set its domain to a super domain by calling
2076 // document.setDomain(...). In these cases, the binding code can reset
2077 // a context's security token to its global object so that the fast access
2078 // check will always fail.
2079
2080 // Check if the current execution context can access a target frame.
2081 // First it checks same domain policy using the lexical context
2082 //
2083 // This is equivalent to KJS::Window::allowsAccessFrom(ExecState*, String&).
2084 bool V8Proxy::CanAccessPrivate(DOMWindow* target_window)
2085 {
2086 ASSERT(target_window);
2087
2088 String message;
2089
2090 DOMWindow* origin_window = retrieveWindow();
2091 if (origin_window == target_window)
2092 return true;
2093
2094 if (!origin_window)
2095 return false;
2096
2097 const SecurityOrigin* active_security_origin = origin_window->securityOrigin ();
2098 const SecurityOrigin* target_security_origin = target_window->securityOrigin ();
2099
2100 // We have seen crashes were the security origin of the target has not been
2101 // initialized. Defend against that.
2102 if (!target_security_origin)
2103 return false;
2104
2105 if (active_security_origin->canAccess(target_security_origin))
2106 return true;
2107
2108 // Allow access to a "about:blank" page if the dynamic context is a
2109 // detached context of the same frame as the blank page.
2110 if (target_security_origin->isEmpty() &&
2111 origin_window->frame() == target_window->frame())
2112 return true;
2113
2114 return false;
2115 }
2116
2117
2118 bool V8Proxy::CanAccessFrame(Frame* target, bool report_error)
2119 {
2120 // The subject is detached from a frame, deny accesses.
2121 if (!target)
2122 return false;
2123
2124 if (!CanAccessPrivate(target->domWindow())) {
2125 if (report_error)
2126 ReportUnsafeAccessTo(target, REPORT_NOW);
2127 return false;
2128 }
2129 return true;
2130 }
2131
2132
2133 bool V8Proxy::CheckNodeSecurity(Node* node)
2134 {
2135 if (!node)
2136 return false;
2137
2138 Frame* target = node->document()->frame();
2139
2140 if (!target)
2141 return false;
2142
2143 return CanAccessFrame(target, true);
2144 }
2145
2146 v8::Persistent<v8::Context> V8Proxy::createNewContext(
2147 v8::Handle<v8::Object> global)
2148 {
2149 v8::Persistent<v8::Context> result;
2150
2151 // Create a new environment using an empty template for the shadow
2152 // object. Reuse the global object if one has been created earlier.
2153 v8::Persistent<v8::ObjectTemplate> globalTemplate =
2154 V8DOMWindow::GetShadowObjectTemplate();
2155 if (globalTemplate.IsEmpty())
2156 return result;
2157
2158 // Install a security handler with V8.
2159 globalTemplate->SetAccessCheckCallbacks(
2160 V8Custom::v8DOMWindowNamedSecurityCheck,
2161 V8Custom::v8DOMWindowIndexedSecurityCheck,
2162 v8::Integer::New(V8ClassIndex::DOMWINDOW));
2163
2164 // Dynamically tell v8 about our extensions now.
2165 const char** extensionNames = new const char*[m_extensions.size()];
2166 int index = 0;
2167 for (V8ExtensionList::iterator it = m_extensions.begin();
2168 it != m_extensions.end(); ++it) {
2169 // Note: we check the loader URL here instead of the document URL
2170 // because we might be currently loading an URL into a blank page.
2171 // See http://code.google.com/p/chromium/issues/detail?id=10924
2172 if (it->scheme.length() > 0 &&
2173 (it->scheme != m_frame->loader()->activeDocumentLoader()->url().prot ocol() ||
2174 it->scheme != m_frame->page()->mainFrame()->loader()->activeDocumen tLoader()->url().protocol()))
2175 continue;
2176
2177 extensionNames[index++] = it->extension->name();
2178 }
2179 v8::ExtensionConfiguration extensions(index, extensionNames);
2180 result = v8::Context::New(&extensions, globalTemplate, global);
2181 delete [] extensionNames;
2182 extensionNames = 0;
2183
2184 return result;
2185 }
2186
2187 bool V8Proxy::installDOMWindow(v8::Handle<v8::Context> context,
2188 DOMWindow* window)
2189 {
2190 v8::Handle<v8::String> implicit_proto_string = v8::String::New("__proto__");
2191 if (implicit_proto_string.IsEmpty())
2192 return false;
2193
2194 // Create a new JS window object and use it as the prototype for the
2195 // shadow global object.
2196 v8::Handle<v8::Function> window_constructor =
2197 GetConstructor(V8ClassIndex::DOMWINDOW);
2198 v8::Local<v8::Object> js_window =
2199 SafeAllocation::NewInstance(window_constructor);
2200 // Bail out if allocation failed.
2201 if (js_window.IsEmpty())
2202 return false;
2203
2204 // Wrap the window.
2205 SetDOMWrapper(js_window,
2206 V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW),
2207 window);
2208
2209 window->ref();
2210 V8Proxy::SetJSWrapperForDOMObject(window,
2211 v8::Persistent<v8::Object>::New(js_window));
2212
2213 // Insert the window instance as the prototype of the shadow object.
2214 v8::Handle<v8::Object> v8_global = context->Global();
2215 v8_global->Set(implicit_proto_string, js_window);
2216 return true;
2217 }
2218
2219 // Create a new environment and setup the global object.
2220 //
2221 // The global object corresponds to a DOMWindow instance. However, to
2222 // allow properties of the JS DOMWindow instance to be shadowed, we
2223 // use a shadow object as the global object and use the JS DOMWindow
2224 // instance as the prototype for that shadow object. The JS DOMWindow
2225 // instance is undetectable from javascript code because the __proto__
2226 // accessors skip that object.
2227 //
2228 // The shadow object and the DOMWindow instance are seen as one object
2229 // from javascript. The javascript object that corresponds to a
2230 // DOMWindow instance is the shadow object. When mapping a DOMWindow
2231 // instance to a V8 object, we return the shadow object.
2232 //
2233 // To implement split-window, see
2234 // 1) https://bugs.webkit.org/show_bug.cgi?id=17249
2235 // 2) https://wiki.mozilla.org/Gecko:SplitWindow
2236 // 3) https://bugzilla.mozilla.org/show_bug.cgi?id=296639
2237 // we need to split the shadow object further into two objects:
2238 // an outer window and an inner window. The inner window is the hidden
2239 // prototype of the outer window. The inner window is the default
2240 // global object of the context. A variable declared in the global
2241 // scope is a property of the inner window.
2242 //
2243 // The outer window sticks to a Frame, it is exposed to JavaScript
2244 // via window.window, window.self, window.parent, etc. The outer window
2245 // has a security token which is the domain. The outer window cannot
2246 // have its own properties. window.foo = 'x' is delegated to the
2247 // inner window.
2248 //
2249 // When a frame navigates to a new page, the inner window is cut off
2250 // the outer window, and the outer window identify is preserved for
2251 // the frame. However, a new inner window is created for the new page.
2252 // If there are JS code holds a closure to the old inner window,
2253 // it won't be able to reach the outer window via its global object.
2254 void V8Proxy::InitContextIfNeeded()
2255 {
2256 // Bail out if the context has already been initialized.
2257 if (!m_context.IsEmpty())
2258 return;
2259
2260 // Create a handle scope for all local handles.
2261 v8::HandleScope handle_scope;
2262
2263 // Setup the security handlers and message listener. This only has
2264 // to be done once.
2265 static bool v8_initialized = false;
2266 if (!v8_initialized) {
2267 // Tells V8 not to call the default OOM handler, binding code
2268 // will handle it.
2269 v8::V8::IgnoreOutOfMemoryException();
2270 v8::V8::SetFatalErrorHandler(ReportFatalErrorInV8);
2271
2272 v8::V8::SetGlobalGCPrologueCallback(&GCPrologue);
2273 v8::V8::SetGlobalGCEpilogueCallback(&GCEpilogue);
2274
2275 v8::V8::AddMessageListener(HandleConsoleMessage);
2276
2277 v8::V8::SetFailedAccessCheckCallbackFunction(ReportUnsafeJavaScriptAccess);
2278
2279 v8_initialized = true;
2280 }
2281
2282 m_context = createNewContext(m_global);
2283 if (m_context.IsEmpty())
2284 return;
2285
2286 // Starting from now, use local context only.
2287 v8::Local<v8::Context> context = GetContext();
2288 v8::Context::Scope context_scope(context);
2289
2290 // Store the first global object created so we can reuse it.
2291 if (m_global.IsEmpty()) {
2292 m_global = v8::Persistent<v8::Object>::New(context->Global());
2293 // Bail out if allocation of the first global objects fails.
2294 if (m_global.IsEmpty()) {
2295 DisposeContextHandles();
2296 return;
2297 }
2298 #ifndef NDEBUG
2299 RegisterGlobalHandle(PROXY, this, m_global);
2300 #endif
2301 }
2302
2303 // Allocate strings used during initialization.
2304 v8::Handle<v8::String> object_string = v8::String::New("Object");
2305 v8::Handle<v8::String> prototype_string = v8::String::New("prototype");
2306 // Bail out if allocation failed.
2307 if (object_string.IsEmpty() ||
2308 prototype_string.IsEmpty()) {
2309 DisposeContextHandles();
2310 return;
2311 }
2312
2313 // Allocate clone cache and pre-allocated objects
2314 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(
2315 m_global->Get(object_string));
2316 m_object_prototype = v8::Persistent<v8::Value>::New(
2317 object->Get(prototype_string));
2318 m_wrapper_boilerplates = v8::Persistent<v8::Array>::New(
2319 v8::Array::New(V8ClassIndex::WRAPPER_TYPE_COUNT));
2320 // Bail out if allocation failed.
2321 if (m_object_prototype.IsEmpty()) {
2322 DisposeContextHandles();
2323 return;
2324 }
2325 #ifndef NDEBUG
2326 RegisterGlobalHandle(PROXY, this, m_object_prototype);
2327 RegisterGlobalHandle(PROXY, this, m_wrapper_boilerplates);
2328 #endif
2329
2330 if (!installDOMWindow(context, m_frame->domWindow()))
2331 DisposeContextHandles();
2332
2333 updateDocument();
2334
2335 SetSecurityToken();
2336
2337 m_frame->loader()->dispatchWindowObjectAvailable();
2338 }
2339
2340 template <class T>
2341 void setDOMExceptionHelper(V8ClassIndex::V8WrapperType type, PassRefPtr<T> excep tion) {
2342 v8::Handle<v8::Value> v8Exception;
2343 if (WorkerContextExecutionProxy::retrieve())
2344 v8Exception = WorkerContextExecutionProxy::ToV8Object(type, exception.get( ));
2345 else
2346 v8Exception = V8Proxy::ToV8Object(type, exception.get());
2347
2348 v8::ThrowException(v8Exception);
2349 }
2350
2351 void V8Proxy::SetDOMException(int exception_code)
2352 {
2353 if (exception_code <= 0)
2354 return;
2355
2356 ExceptionCodeDescription description;
2357 getExceptionCodeDescription(exception_code, description);
2358
2359 v8::Handle<v8::Value> exception;
2360 switch (description.type) {
2361 case DOMExceptionType:
2362 setDOMExceptionHelper(V8ClassIndex::DOMCOREEXCEPTION,
2363 DOMCoreException::create(description));
2364 break;
2365 case RangeExceptionType:
2366 setDOMExceptionHelper(V8ClassIndex::RANGEEXCEPTION,
2367 RangeException::create(description));
2368 break;
2369 case EventExceptionType:
2370 setDOMExceptionHelper(V8ClassIndex::EVENTEXCEPTION,
2371 EventException::create(description));
2372 break;
2373 case XMLHttpRequestExceptionType:
2374 setDOMExceptionHelper(V8ClassIndex::XMLHTTPREQUESTEXCEPTION,
2375 XMLHttpRequestException::create(description));
2376 break;
2377 #if ENABLE(SVG)
2378 case SVGExceptionType:
2379 setDOMExceptionHelper(V8ClassIndex::SVGEXCEPTION,
2380 SVGException::create(description));
2381 break;
2382 #endif
2383 #if ENABLE(XPATH)
2384 case XPathExceptionType:
2385 setDOMExceptionHelper(V8ClassIndex::XPATHEXCEPTION,
2386 XPathException::create(description));
2387 break;
2388 #endif
2389 default:
2390 ASSERT(false);
2391 break;
2392 }
2393 }
2394
2395 v8::Handle<v8::Value> V8Proxy::ThrowError(ErrorType type, const char* message)
2396 {
2397 switch (type) {
2398 case RANGE_ERROR:
2399 return v8::ThrowException(v8::Exception::RangeError(v8String(message)));
2400 case REFERENCE_ERROR:
2401 return v8::ThrowException(
2402 v8::Exception::ReferenceError(v8String(message)));
2403 case SYNTAX_ERROR:
2404 return v8::ThrowException(v8::Exception::SyntaxError(v8String(message)));
2405 case TYPE_ERROR:
2406 return v8::ThrowException(v8::Exception::TypeError(v8String(message)));
2407 case GENERAL_ERROR:
2408 return v8::ThrowException(v8::Exception::Error(v8String(message)));
2409 default:
2410 ASSERT(false);
2411 return v8::Handle<v8::Value>();
2412 }
2413 }
2414
2415 v8::Local<v8::Context> V8Proxy::GetContext(Frame* frame)
2416 {
2417 V8Proxy* proxy = retrieve(frame);
2418 if (!proxy)
2419 return v8::Local<v8::Context>();
2420
2421 proxy->InitContextIfNeeded();
2422 return proxy->GetContext();
2423 }
2424
2425 v8::Local<v8::Context> V8Proxy::GetCurrentContext()
2426 {
2427 return v8::Context::GetCurrent();
2428 }
2429
2430 v8::Handle<v8::Value> V8Proxy::ToV8Object(V8ClassIndex::V8WrapperType type, void * imp)
2431 {
2432 ASSERT(type != V8ClassIndex::EVENTLISTENER);
2433 ASSERT(type != V8ClassIndex::EVENTTARGET);
2434 ASSERT(type != V8ClassIndex::EVENT);
2435
2436 bool is_active_dom_object = false;
2437 switch (type) {
2438 #define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE:
2439 DOM_NODE_TYPES(MAKE_CASE)
2440 #if ENABLE(SVG)
2441 SVG_NODE_TYPES(MAKE_CASE)
2442 #endif
2443 return NodeToV8Object(static_cast<Node*>(imp));
2444 case V8ClassIndex::CSSVALUE:
2445 return CSSValueToV8Object(static_cast<CSSValue*>(imp));
2446 case V8ClassIndex::CSSRULE:
2447 return CSSRuleToV8Object(static_cast<CSSRule*>(imp));
2448 case V8ClassIndex::STYLESHEET:
2449 return StyleSheetToV8Object(static_cast<StyleSheet*>(imp));
2450 case V8ClassIndex::DOMWINDOW:
2451 return WindowToV8Object(static_cast<DOMWindow*>(imp));
2452 #if ENABLE(SVG)
2453 SVG_NONNODE_TYPES(MAKE_CASE)
2454 if (type == V8ClassIndex::SVGELEMENTINSTANCE)
2455 return SVGElementInstanceToV8Object(static_cast<SVGElementInstance*>(imp ));
2456 return SVGObjectWithContextToV8Object(type, imp);
2457 #endif
2458
2459 ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE)
2460 is_active_dom_object = true;
2461 break;
2462 default:
2463 break;
2464 }
2465
2466 #undef MAKE_CASE
2467
2468 if (!imp) return v8::Null();
2469
2470 // Non DOM node
2471 v8::Persistent<v8::Object> result = is_active_dom_object ?
2472 getActiveDOMObjectMap().get(imp) :
2473 getDOMObjectMap().get(imp);
2474 if (result.IsEmpty()) {
2475 v8::Local<v8::Object> v8obj = InstantiateV8Object(type, type, imp);
2476 if (!v8obj.IsEmpty()) {
2477 // Go through big switch statement, it has some duplications
2478 // that were handled by code above (such as CSSVALUE, CSSRULE, etc).
2479 switch (type) {
2480 #define MAKE_CASE(TYPE, NAME) \
2481 case V8ClassIndex::TYPE: static_cast<NAME*>(imp)->ref(); break;
2482 DOM_OBJECT_TYPES(MAKE_CASE)
2483 #undef MAKE_CASE
2484 default:
2485 ASSERT(false);
2486 }
2487 result = v8::Persistent<v8::Object>::New(v8obj);
2488 if (is_active_dom_object)
2489 SetJSWrapperForActiveDOMObject(imp, result);
2490 else
2491 SetJSWrapperForDOMObject(imp, result);
2492
2493 // Special case for non-node objects associated with a
2494 // DOMWindow. Both Safari and FF let the JS wrappers for these
2495 // objects survive GC. To mimic their behavior, V8 creates
2496 // hidden references from the DOMWindow to these wrapper
2497 // objects. These references get cleared when the DOMWindow is
2498 // reused by a new page.
2499 switch (type) {
2500 case V8ClassIndex::CONSOLE:
2501 SetHiddenWindowReference(static_cast<Console*>(imp)->frame(),
2502 V8Custom::kDOMWindowConsoleIndex, result);
2503 break;
2504 case V8ClassIndex::HISTORY:
2505 SetHiddenWindowReference(static_cast<History*>(imp)->frame(),
2506 V8Custom::kDOMWindowHistoryIndex, result);
2507 break;
2508 case V8ClassIndex::NAVIGATOR:
2509 SetHiddenWindowReference(static_cast<Navigator*>(imp)->frame(),
2510 V8Custom::kDOMWindowNavigatorIndex, result);
2511 break;
2512 case V8ClassIndex::SCREEN:
2513 SetHiddenWindowReference(static_cast<Screen*>(imp)->frame(),
2514 V8Custom::kDOMWindowScreenIndex, result);
2515 break;
2516 case V8ClassIndex::LOCATION:
2517 SetHiddenWindowReference(static_cast<Location*>(imp)->frame(),
2518 V8Custom::kDOMWindowLocationIndex, result);
2519 break;
2520 case V8ClassIndex::DOMSELECTION:
2521 SetHiddenWindowReference(static_cast<DOMSelection*>(imp)->frame(),
2522 V8Custom::kDOMWindowDOMSelectionIndex, result );
2523 break;
2524 case V8ClassIndex::BARINFO: {
2525 BarInfo* barinfo = static_cast<BarInfo*>(imp);
2526 Frame* frame = barinfo->frame();
2527 switch (barinfo->type()) {
2528 case BarInfo::Locationbar:
2529 SetHiddenWindowReference(frame, V8Custom::kDOMWindowLocationbarInd ex, result);
2530 break;
2531 case BarInfo::Menubar:
2532 SetHiddenWindowReference(frame, V8Custom::kDOMWindowMenubarIndex, result);
2533 break;
2534 case BarInfo::Personalbar:
2535 SetHiddenWindowReference(frame, V8Custom::kDOMWindowPersonalbarInd ex, result);
2536 break;
2537 case BarInfo::Scrollbars:
2538 SetHiddenWindowReference(frame, V8Custom::kDOMWindowScrollbarsInde x, result);
2539 break;
2540 case BarInfo::Statusbar:
2541 SetHiddenWindowReference(frame, V8Custom::kDOMWindowStatusbarIndex , result);
2542 break;
2543 case BarInfo::Toolbar:
2544 SetHiddenWindowReference(frame, V8Custom::kDOMWindowToolbarIndex, result);
2545 break;
2546 }
2547 break;
2548 }
2549 default:
2550 break;
2551 }
2552 }
2553 }
2554 return result;
2555 }
2556
2557
2558 void V8Proxy::SetHiddenWindowReference(Frame* frame,
2559 const int internal_index,
2560 v8::Handle<v8::Object> jsobj)
2561 {
2562 // Get DOMWindow
2563 if (!frame) return; // Object might be detached from window
2564 v8::Handle<v8::Context> context = GetContext(frame);
2565 if (context.IsEmpty()) return;
2566
2567 ASSERT(internal_index < V8Custom::kDOMWindowInternalFieldCount);
2568
2569 v8::Handle<v8::Object> global = context->Global();
2570 // Look for real DOM wrapper.
2571 global = LookupDOMWrapper(V8ClassIndex::DOMWINDOW, global);
2572 ASSERT(!global.IsEmpty());
2573 ASSERT(global->GetInternalField(internal_index)->IsUndefined());
2574 global->SetInternalField(internal_index, jsobj);
2575 }
2576
2577
2578 V8ClassIndex::V8WrapperType V8Proxy::GetDOMWrapperType(v8::Handle<v8::Object> ob ject)
2579 {
2580 ASSERT(MaybeDOMWrapper(object));
2581 v8::Handle<v8::Value> type =
2582 object->GetInternalField(V8Custom::kDOMWrapperTypeIndex);
2583 return V8ClassIndex::FromInt(type->Int32Value());
2584 }
2585
2586
2587 void* V8Proxy::ToNativeObjectImpl(V8ClassIndex::V8WrapperType type,
2588 v8::Handle<v8::Value> object)
2589 {
2590 // Native event listener is per frame, it cannot be handled
2591 // by this generic function.
2592 ASSERT(type != V8ClassIndex::EVENTLISTENER);
2593 ASSERT(type != V8ClassIndex::EVENTTARGET);
2594
2595 ASSERT(MaybeDOMWrapper(object));
2596
2597 switch (type) {
2598 #define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE:
2599 DOM_NODE_TYPES(MAKE_CASE)
2600 #if ENABLE(SVG)
2601 SVG_NODE_TYPES(MAKE_CASE)
2602 #endif
2603 ASSERT(false);
2604 return NULL;
2605 case V8ClassIndex::XMLHTTPREQUEST:
2606 return DOMWrapperToNative<XMLHttpRequest>(object);
2607 case V8ClassIndex::EVENT:
2608 return DOMWrapperToNative<Event>(object);
2609 case V8ClassIndex::CSSRULE:
2610 return DOMWrapperToNative<CSSRule>(object);
2611 default:
2612 break;
2613 }
2614 #undef MAKE_CASE
2615
2616 return DOMWrapperToNative<void>(object);
2617 }
2618
2619
2620 void* V8Proxy::ToSVGPODTypeImpl(V8ClassIndex::V8WrapperType type,
2621 v8::Handle<v8::Value> object) {
2622 return IsWrapperOfType(object, type)
2623 ? DOMWrapperToNative<void>(object)
2624 : NULL;
2625 }
2626
2627
2628 v8::Handle<v8::Object> V8Proxy::LookupDOMWrapper(
2629 V8ClassIndex::V8WrapperType type, v8::Handle<v8::Value> value)
2630 {
2631 if (value.IsEmpty())
2632 return v8::Handle<v8::Object>();
2633
2634 v8::Handle<v8::FunctionTemplate> desc = V8Proxy::GetTemplate(type);
2635 while (value->IsObject()) {
2636 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
2637 if (desc->HasInstance(object))
2638 return object;
2639
2640 value = object->GetPrototype();
2641 }
2642 return v8::Handle<v8::Object>();
2643 }
2644
2645
2646 // static
2647 void* V8Proxy::DOMWrapperToNodeHelper(v8::Handle<v8::Value> value) {
2648 ASSERT(MaybeDOMWrapper(value));
2649
2650 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
2651
2652 ASSERT(GetDOMWrapperType(object) == V8ClassIndex::NODE);
2653
2654 v8::Handle<v8::Value> wrapper =
2655 object->GetInternalField(V8Custom::kDOMWrapperObjectIndex);
2656 return ExtractCPointer<Node>(wrapper);
2657 }
2658
2659
2660 PassRefPtr<NodeFilter> V8Proxy::ToNativeNodeFilter(v8::Handle<v8::Value> filter)
2661 {
2662 // A NodeFilter is used when walking through a DOM tree or iterating tree
2663 // nodes.
2664 // TODO: we may want to cache NodeFilterCondition and NodeFilter
2665 // object, but it is minor.
2666 // NodeFilter is passed to NodeIterator that has a ref counted pointer
2667 // to NodeFilter. NodeFilter has a ref counted pointer to NodeFilterConditio n.
2668 // In NodeFilterCondition, filter object is persisted in its constructor,
2669 // and disposed in its destructor.
2670 if (!filter->IsFunction())
2671 return 0;
2672
2673 NodeFilterCondition* cond = new V8NodeFilterCondition(filter);
2674 return NodeFilter::create(cond);
2675 }
2676
2677
2678 v8::Local<v8::Object> V8Proxy::InstantiateV8Object(
2679 V8ClassIndex::V8WrapperType desc_type,
2680 V8ClassIndex::V8WrapperType cptr_type,
2681 void* imp)
2682 {
2683 // Make a special case for document.all
2684 if (desc_type == V8ClassIndex::HTMLCOLLECTION &&
2685 static_cast<HTMLCollection*>(imp)->type() == DocAll) {
2686 desc_type = V8ClassIndex::UNDETECTABLEHTMLCOLLECTION;
2687 }
2688
2689 V8Proxy* proxy = V8Proxy::retrieve();
2690 v8::Local<v8::Object> instance;
2691 if (proxy) {
2692 instance = proxy->CreateWrapperFromCache(desc_type);
2693 } else {
2694 v8::Local<v8::Function> function = GetTemplate(desc_type)->GetFunction();
2695 instance = SafeAllocation::NewInstance(function);
2696 }
2697 if (!instance.IsEmpty()) {
2698 // Avoid setting the DOM wrapper for failed allocations.
2699 SetDOMWrapper(instance, V8ClassIndex::ToInt(cptr_type), imp);
2700 }
2701 return instance;
2702 }
2703
2704 v8::Handle<v8::Value> V8Proxy::CheckNewLegal(const v8::Arguments& args)
2705 {
2706 if (!AllowAllocation::m_current)
2707 return ThrowError(TYPE_ERROR, "Illegal constructor");
2708
2709 return args.This();
2710 }
2711
2712 void V8Proxy::SetDOMWrapper(v8::Handle<v8::Object> obj, int type, void* cptr)
2713 {
2714 ASSERT(obj->InternalFieldCount() >= 2);
2715 obj->SetInternalField(V8Custom::kDOMWrapperObjectIndex, WrapCPointer(cptr));
2716 obj->SetInternalField(V8Custom::kDOMWrapperTypeIndex, v8::Integer::New(type));
2717 }
2718
2719
2720 #ifndef NDEBUG
2721 bool V8Proxy::MaybeDOMWrapper(v8::Handle<v8::Value> value)
2722 {
2723 if (value.IsEmpty() || !value->IsObject()) return false;
2724
2725 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(value);
2726 if (obj->InternalFieldCount() == 0) return false;
2727
2728 ASSERT(obj->InternalFieldCount() >=
2729 V8Custom::kDefaultWrapperInternalFieldCount);
2730
2731 v8::Handle<v8::Value> type =
2732 obj->GetInternalField(V8Custom::kDOMWrapperTypeIndex);
2733 ASSERT(type->IsInt32());
2734 ASSERT(V8ClassIndex::INVALID_CLASS_INDEX < type->Int32Value() &&
2735 type->Int32Value() < V8ClassIndex::CLASSINDEX_END);
2736
2737 v8::Handle<v8::Value> wrapper =
2738 obj->GetInternalField(V8Custom::kDOMWrapperObjectIndex);
2739 ASSERT(wrapper->IsNumber() || wrapper->IsExternal());
2740
2741 return true;
2742 }
2743 #endif
2744
2745
2746 bool V8Proxy::IsDOMEventWrapper(v8::Handle<v8::Value> value)
2747 {
2748 // All kinds of events use EVENT as dom type in JS wrappers.
2749 // See EventToV8Object
2750 return IsWrapperOfType(value, V8ClassIndex::EVENT);
2751 }
2752
2753 bool V8Proxy::IsWrapperOfType(v8::Handle<v8::Value> value,
2754 V8ClassIndex::V8WrapperType classType)
2755 {
2756 if (value.IsEmpty() || !value->IsObject()) return false;
2757
2758 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(value);
2759 if (obj->InternalFieldCount() == 0) return false;
2760
2761 ASSERT(obj->InternalFieldCount() >=
2762 V8Custom::kDefaultWrapperInternalFieldCount);
2763
2764 v8::Handle<v8::Value> wrapper =
2765 obj->GetInternalField(V8Custom::kDOMWrapperObjectIndex);
2766 ASSERT(wrapper->IsNumber() || wrapper->IsExternal());
2767
2768 v8::Handle<v8::Value> type =
2769 obj->GetInternalField(V8Custom::kDOMWrapperTypeIndex);
2770 ASSERT(type->IsInt32());
2771 ASSERT(V8ClassIndex::INVALID_CLASS_INDEX < type->Int32Value() &&
2772 type->Int32Value() < V8ClassIndex::CLASSINDEX_END);
2773
2774 return V8ClassIndex::FromInt(type->Int32Value()) == classType;
2775 }
2776
2777 #if ENABLE(VIDEO)
2778 #define FOR_EACH_VIDEO_TAG(macro) \
2779 macro(audio, AUDIO) \
2780 macro(source, SOURCE) \
2781 macro(video, VIDEO)
2782 #else
2783 #define FOR_EACH_VIDEO_TAG(macro)
2784 #endif
2785
2786 #define FOR_EACH_TAG(macro) \
2787 macro(a, ANCHOR) \
2788 macro(applet, APPLET) \
2789 macro(area, AREA) \
2790 macro(base, BASE) \
2791 macro(basefont, BASEFONT) \
2792 macro(blockquote, BLOCKQUOTE) \
2793 macro(body, BODY) \
2794 macro(br, BR) \
2795 macro(button, BUTTON) \
2796 macro(caption, TABLECAPTION) \
2797 macro(col, TABLECOL) \
2798 macro(colgroup, TABLECOL) \
2799 macro(del, MOD) \
2800 macro(canvas, CANVAS) \
2801 macro(dir, DIRECTORY) \
2802 macro(div, DIV) \
2803 macro(dl, DLIST) \
2804 macro(embed, EMBED) \
2805 macro(fieldset, FIELDSET) \
2806 macro(font, FONT) \
2807 macro(form, FORM) \
2808 macro(frame, FRAME) \
2809 macro(frameset, FRAMESET) \
2810 macro(h1, HEADING) \
2811 macro(h2, HEADING) \
2812 macro(h3, HEADING) \
2813 macro(h4, HEADING) \
2814 macro(h5, HEADING) \
2815 macro(h6, HEADING) \
2816 macro(head, HEAD) \
2817 macro(hr, HR) \
2818 macro(html, HTML) \
2819 macro(img, IMAGE) \
2820 macro(iframe, IFRAME) \
2821 macro(image, IMAGE) \
2822 macro(input, INPUT) \
2823 macro(ins, MOD) \
2824 macro(isindex, ISINDEX) \
2825 macro(keygen, SELECT) \
2826 macro(label, LABEL) \
2827 macro(legend, LEGEND) \
2828 macro(li, LI) \
2829 macro(link, LINK) \
2830 macro(listing, PRE) \
2831 macro(map, MAP) \
2832 macro(marquee, MARQUEE) \
2833 macro(menu, MENU) \
2834 macro(meta, META) \
2835 macro(object, OBJECT) \
2836 macro(ol, OLIST) \
2837 macro(optgroup, OPTGROUP) \
2838 macro(option, OPTION) \
2839 macro(p, PARAGRAPH) \
2840 macro(param, PARAM) \
2841 macro(pre, PRE) \
2842 macro(q, QUOTE) \
2843 macro(script, SCRIPT) \
2844 macro(select, SELECT) \
2845 macro(style, STYLE) \
2846 macro(table, TABLE) \
2847 macro(thead, TABLESECTION) \
2848 macro(tbody, TABLESECTION) \
2849 macro(tfoot, TABLESECTION) \
2850 macro(td, TABLECELL) \
2851 macro(th, TABLECELL) \
2852 macro(tr, TABLEROW) \
2853 macro(textarea, TEXTAREA) \
2854 macro(title, TITLE) \
2855 macro(ul, ULIST) \
2856 macro(xmp, PRE)
2857
2858 V8ClassIndex::V8WrapperType V8Proxy::GetHTMLElementType(HTMLElement* element)
2859 {
2860 static HashMap<String, V8ClassIndex::V8WrapperType> map;
2861 if (map.isEmpty()) {
2862 #define ADD_TO_HASH_MAP(tag, name) \
2863 map.set(#tag, V8ClassIndex::HTML##name##ELEMENT);
2864 FOR_EACH_TAG(ADD_TO_HASH_MAP)
2865 #if ENABLE(VIDEO)
2866 if (MediaPlayer::isAvailable()) {
2867 FOR_EACH_VIDEO_TAG(ADD_TO_HASH_MAP)
2868 }
2869 #endif
2870 #undef ADD_TO_HASH_MAP
2871 }
2872
2873 V8ClassIndex::V8WrapperType t = map.get(element->localName().impl());
2874 if (t == 0)
2875 return V8ClassIndex::HTMLELEMENT;
2876 return t;
2877 }
2878 #undef FOR_EACH_TAG
2879
2880 #if ENABLE(SVG)
2881
2882 #if ENABLE(SVG_ANIMATION)
2883 #define FOR_EACH_ANIMATION_TAG(macro) \
2884 macro(animateColor, ANIMATECOLOR) \
2885 macro(animate, ANIMATE) \
2886 macro(animateTransform, ANIMATETRANSFORM) \
2887 macro(set, SET)
2888 #else
2889 #define FOR_EACH_ANIMATION_TAG(macro)
2890 #endif
2891
2892 #if ENABLE(SVG_FILTERS)
2893 #define FOR_EACH_FILTERS_TAG(macro) \
2894 macro(feBlend, FEBLEND) \
2895 macro(feColorMatrix, FECOLORMATRIX) \
2896 macro(feComponentTransfer, FECOMPONENTTRANSFER) \
2897 macro(feComposite, FECOMPOSITE) \
2898 macro(feDiffuseLighting, FEDIFFUSELIGHTING) \
2899 macro(feDisplacementMap, FEDISPLACEMENTMAP) \
2900 macro(feDistantLight, FEDISTANTLIGHT) \
2901 macro(feFlood, FEFLOOD) \
2902 macro(feFuncA, FEFUNCA) \
2903 macro(feFuncB, FEFUNCB) \
2904 macro(feFuncG, FEFUNCG) \
2905 macro(feFuncR, FEFUNCR) \
2906 macro(feGaussianBlur, FEGAUSSIANBLUR) \
2907 macro(feImage, FEIMAGE) \
2908 macro(feMerge, FEMERGE) \
2909 macro(feMergeNode, FEMERGENODE) \
2910 macro(feOffset, FEOFFSET) \
2911 macro(fePointLight, FEPOINTLIGHT) \
2912 macro(feSpecularLighting, FESPECULARLIGHTING) \
2913 macro(feSpotLight, FESPOTLIGHT) \
2914 macro(feTile, FETILE) \
2915 macro(feTurbulence, FETURBULENCE) \
2916 macro(filter, FILTER)
2917 #else
2918 #define FOR_EACH_FILTERS_TAG(macro)
2919 #endif
2920
2921 #if ENABLE(SVG_FONTS)
2922 #define FOR_EACH_FONTS_TAG(macro) \
2923 macro(definition-src, DEFINITIONSRC) \
2924 macro(font-face, FONTFACE) \
2925 macro(font-face-format, FONTFACEFORMAT) \
2926 macro(font-face-name, FONTFACENAME) \
2927 macro(font-face-src, FONTFACESRC) \
2928 macro(font-face-uri, FONTFACEURI)
2929 #else
2930 #define FOR_EACH_FONTS_TAG(marco)
2931 #endif
2932
2933 #if ENABLE(SVG_FOREIGN_OBJECT)
2934 #define FOR_EACH_FOREIGN_OBJECT_TAG(macro) \
2935 macro(foreignObject, FOREIGNOBJECT)
2936 #else
2937 #define FOR_EACH_FOREIGN_OBJECT_TAG(macro)
2938 #endif
2939
2940 #if ENABLE(SVG_USE)
2941 #define FOR_EACH_USE_TAG(macro) \
2942 macro(use, USE)
2943 #else
2944 #define FOR_EACH_USE_TAG(macro)
2945 #endif
2946
2947 #define FOR_EACH_TAG(macro) \
2948 FOR_EACH_ANIMATION_TAG(macro) \
2949 FOR_EACH_FILTERS_TAG(macro) \
2950 FOR_EACH_FONTS_TAG(macro) \
2951 FOR_EACH_FOREIGN_OBJECT_TAG(macro) \
2952 FOR_EACH_USE_TAG(macro) \
2953 macro(a, A) \
2954 macro(altGlyph, ALTGLYPH) \
2955 macro(circle, CIRCLE) \
2956 macro(clipPath, CLIPPATH) \
2957 macro(cursor, CURSOR) \
2958 macro(defs, DEFS) \
2959 macro(desc, DESC) \
2960 macro(ellipse, ELLIPSE) \
2961 macro(g, G) \
2962 macro(glyph, GLYPH) \
2963 macro(image, IMAGE) \
2964 macro(linearGradient, LINEARGRADIENT) \
2965 macro(line, LINE) \
2966 macro(marker, MARKER) \
2967 macro(mask, MASK) \
2968 macro(metadata, METADATA) \
2969 macro(path, PATH) \
2970 macro(pattern, PATTERN) \
2971 macro(polyline, POLYLINE) \
2972 macro(polygon, POLYGON) \
2973 macro(radialGradient, RADIALGRADIENT) \
2974 macro(rect, RECT) \
2975 macro(script, SCRIPT) \
2976 macro(stop, STOP) \
2977 macro(style, STYLE) \
2978 macro(svg, SVG) \
2979 macro(switch, SWITCH) \
2980 macro(symbol, SYMBOL) \
2981 macro(text, TEXT) \
2982 macro(textPath, TEXTPATH) \
2983 macro(title, TITLE) \
2984 macro(tref, TREF) \
2985 macro(tspan, TSPAN) \
2986 macro(view, VIEW) \
2987 // end of macro
2988
2989 V8ClassIndex::V8WrapperType V8Proxy::GetSVGElementType(SVGElement* element)
2990 {
2991 static HashMap<String, V8ClassIndex::V8WrapperType> map;
2992 if (map.isEmpty()) {
2993 #define ADD_TO_HASH_MAP(tag, name) \
2994 map.set(#tag, V8ClassIndex::SVG##name##ELEMENT);
2995 FOR_EACH_TAG(ADD_TO_HASH_MAP)
2996 #undef ADD_TO_HASH_MAP
2997 }
2998
2999 V8ClassIndex::V8WrapperType t = map.get(element->localName().impl());
3000 if (t == 0) return V8ClassIndex::SVGELEMENT;
3001 return t;
3002 }
3003 #undef FOR_EACH_TAG
3004
3005 #endif // ENABLE(SVG)
3006
3007
3008 v8::Handle<v8::Value> V8Proxy::EventToV8Object(Event* event)
3009 {
3010 if (!event)
3011 return v8::Null();
3012
3013 v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(event);
3014 if (!wrapper.IsEmpty())
3015 return wrapper;
3016
3017 V8ClassIndex::V8WrapperType type = V8ClassIndex::EVENT;
3018
3019 if (event->isUIEvent()) {
3020 if (event->isKeyboardEvent())
3021 type = V8ClassIndex::KEYBOARDEVENT;
3022 else if (event->isTextEvent())
3023 type = V8ClassIndex::TEXTEVENT;
3024 else if (event->isMouseEvent())
3025 type = V8ClassIndex::MOUSEEVENT;
3026 else if (event->isWheelEvent())
3027 type = V8ClassIndex::WHEELEVENT;
3028 #if ENABLE(SVG)
3029 else if (event->isSVGZoomEvent())
3030 type = V8ClassIndex::SVGZOOMEVENT;
3031 #endif
3032 else
3033 type = V8ClassIndex::UIEVENT;
3034 } else if (event->isMutationEvent())
3035 type = V8ClassIndex::MUTATIONEVENT;
3036 else if (event->isOverflowEvent())
3037 type = V8ClassIndex::OVERFLOWEVENT;
3038 else if (event->isMessageEvent())
3039 type = V8ClassIndex::MESSAGEEVENT;
3040 else if (event->isProgressEvent()) {
3041 if (event->isXMLHttpRequestProgressEvent())
3042 type = V8ClassIndex::XMLHTTPREQUESTPROGRESSEVENT;
3043 else
3044 type = V8ClassIndex::PROGRESSEVENT;
3045 } else if (event->isWebKitAnimationEvent())
3046 type = V8ClassIndex::WEBKITANIMATIONEVENT;
3047 else if (event->isWebKitTransitionEvent())
3048 type = V8ClassIndex::WEBKITTRANSITIONEVENT;
3049
3050
3051 v8::Handle<v8::Object> result =
3052 InstantiateV8Object(type, V8ClassIndex::EVENT, event);
3053 if (result.IsEmpty()) {
3054 // Instantiation failed. Avoid updating the DOM object map and
3055 // return null which is already handled by callers of this function
3056 // in case the event is NULL.
3057 return v8::Null();
3058 }
3059
3060 event->ref(); // fast ref
3061 SetJSWrapperForDOMObject(event, v8::Persistent<v8::Object>::New(result));
3062
3063 return result;
3064 }
3065
3066
3067 // Caller checks node is not null.
3068 v8::Handle<v8::Value> V8Proxy::NodeToV8Object(Node* node)
3069 {
3070 if (!node) return v8::Null();
3071
3072 // Find the context to which the node belongs and create the wrapper
3073 // in that context. If the node is not in a document, the current
3074 // context is used.
3075 //
3076 // Getting the context might initialize the context which can instantiate
3077 // a document wrapper. Therefore, we get the context before checking if
3078 // the node already has a wrapper.
3079 v8::Local<v8::Context> context;
3080 Document* doc = node->document();
3081 if (doc) {
3082 context = V8Proxy::GetContext(doc->frame());
3083 }
3084
3085 v8::Handle<v8::Object> wrapper = getDOMNodeMap().get(node);
3086 if (!wrapper.IsEmpty())
3087 return wrapper;
3088
3089 bool is_document = false; // document type node has special handling
3090 V8ClassIndex::V8WrapperType type;
3091
3092 switch (node->nodeType()) {
3093 case Node::ELEMENT_NODE:
3094 if (node->isHTMLElement())
3095 type = GetHTMLElementType(static_cast<HTMLElement*>(node));
3096 #if ENABLE(SVG)
3097 else if (node->isSVGElement())
3098 type = GetSVGElementType(static_cast<SVGElement*>(node));
3099 #endif
3100 else
3101 type = V8ClassIndex::ELEMENT;
3102 break;
3103 case Node::ATTRIBUTE_NODE:
3104 type = V8ClassIndex::ATTR;
3105 break;
3106 case Node::TEXT_NODE:
3107 type = V8ClassIndex::TEXT;
3108 break;
3109 case Node::CDATA_SECTION_NODE:
3110 type = V8ClassIndex::CDATASECTION;
3111 break;
3112 case Node::ENTITY_NODE:
3113 type = V8ClassIndex::ENTITY;
3114 break;
3115 case Node::PROCESSING_INSTRUCTION_NODE:
3116 type = V8ClassIndex::PROCESSINGINSTRUCTION;
3117 break;
3118 case Node::COMMENT_NODE:
3119 type = V8ClassIndex::COMMENT;
3120 break;
3121 case Node::DOCUMENT_NODE: {
3122 is_document = true;
3123 Document* doc = static_cast<Document*>(node);
3124 if (doc->isHTMLDocument())
3125 type = V8ClassIndex::HTMLDOCUMENT;
3126 #if ENABLE(SVG)
3127 else if (doc->isSVGDocument())
3128 type = V8ClassIndex::SVGDOCUMENT;
3129 #endif
3130 else
3131 type = V8ClassIndex::DOCUMENT;
3132 break;
3133 }
3134 case Node::DOCUMENT_TYPE_NODE:
3135 type = V8ClassIndex::DOCUMENTTYPE;
3136 break;
3137 case Node::NOTATION_NODE:
3138 type = V8ClassIndex::NOTATION;
3139 break;
3140 case Node::DOCUMENT_FRAGMENT_NODE:
3141 type = V8ClassIndex::DOCUMENTFRAGMENT;
3142 break;
3143 case Node::ENTITY_REFERENCE_NODE:
3144 type = V8ClassIndex::ENTITYREFERENCE;
3145 break;
3146 default:
3147 type = V8ClassIndex::NODE;
3148 }
3149
3150 // Enter the node's context and create the wrapper in that context.
3151 if (!context.IsEmpty()) {
3152 context->Enter();
3153 }
3154
3155 v8::Local<v8::Object> result =
3156 InstantiateV8Object(type, V8ClassIndex::NODE, node);
3157
3158 // Exit the node's context if it was entered.
3159 if (!context.IsEmpty()) {
3160 context->Exit();
3161 }
3162
3163 if (result.IsEmpty()) {
3164 // If instantiation failed it's important not to add the result
3165 // to the DOM node map. Instead we return an empty handle, which
3166 // should already be handled by callers of this function in case
3167 // the node is NULL.
3168 return result;
3169 }
3170
3171 node->ref();
3172 SetJSWrapperForDOMNode(node, v8::Persistent<v8::Object>::New(result));
3173
3174 if (is_document) {
3175 Document* doc = static_cast<Document*>(node);
3176 V8Proxy* proxy = V8Proxy::retrieve(doc->frame());
3177 if (proxy)
3178 proxy->UpdateDocumentWrapper(result);
3179
3180 if (type == V8ClassIndex::HTMLDOCUMENT) {
3181 // Create marker object and insert it in two internal fields.
3182 // This is used to implement temporary shadowing of
3183 // document.all.
3184 ASSERT(result->InternalFieldCount() ==
3185 V8Custom::kHTMLDocumentInternalFieldCount);
3186 v8::Local<v8::Object> marker = v8::Object::New();
3187 result->SetInternalField(V8Custom::kHTMLDocumentMarkerIndex, marker);
3188 result->SetInternalField(V8Custom::kHTMLDocumentShadowIndex, marker);
3189 }
3190 }
3191
3192 return result;
3193 }
3194
3195
3196 // A JS object of type EventTarget can only be the following possible types:
3197 // 1) EventTargetNode; 2) DOMWindow 3) XMLHttpRequest; 4) MessagePort;
3198 // 5) XMLHttpRequestUpload
3199 // check EventTarget.h for new type conversion methods
3200 v8::Handle<v8::Value> V8Proxy::EventTargetToV8Object(EventTarget* target)
3201 {
3202 if (!target)
3203 return v8::Null();
3204
3205 #if ENABLE(SVG)
3206 SVGElementInstance* instance = target->toSVGElementInstance();
3207 if (instance)
3208 return ToV8Object(V8ClassIndex::SVGELEMENTINSTANCE, instance);
3209 #endif
3210
3211 #if ENABLE(WORKERS)
3212 Worker* worker = target->toWorker();
3213 if (worker)
3214 return ToV8Object(V8ClassIndex::WORKER, worker);
3215 #endif // WORKERS
3216
3217 Node* node = target->toNode();
3218 if (node)
3219 return NodeToV8Object(node);
3220
3221 if (DOMWindow* domWindow = target->toDOMWindow())
3222 return ToV8Object(V8ClassIndex::DOMWINDOW, domWindow);
3223
3224 // XMLHttpRequest is created within its JS counterpart.
3225 XMLHttpRequest* xhr = target->toXMLHttpRequest();
3226 if (xhr) {
3227 v8::Handle<v8::Object> wrapper = getActiveDOMObjectMap().get(xhr);
3228 ASSERT(!wrapper.IsEmpty());
3229 return wrapper;
3230 }
3231
3232 // MessagePort is created within its JS counterpart
3233 MessagePort* port = target->toMessagePort();
3234 if (port) {
3235 v8::Handle<v8::Object> wrapper = getActiveDOMObjectMap().get(port);
3236 ASSERT(!wrapper.IsEmpty());
3237 return wrapper;
3238 }
3239
3240 XMLHttpRequestUpload* upload = target->toXMLHttpRequestUpload();
3241 if (upload) {
3242 v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(upload);
3243 ASSERT(!wrapper.IsEmpty());
3244 return wrapper;
3245 }
3246
3247 ASSERT(0);
3248 return v8::Handle<v8::Value>();
3249 }
3250
3251
3252 v8::Handle<v8::Value> V8Proxy::EventListenerToV8Object(
3253 EventListener* listener)
3254 {
3255 if (listener == 0) return v8::Null();
3256
3257 // TODO(fqian): can a user take a lazy event listener and set to other places?
3258 V8AbstractEventListener* v8listener =
3259 static_cast<V8AbstractEventListener*>(listener);
3260 return v8listener->getListenerObject();
3261 }
3262
3263
3264 v8::Handle<v8::Value> V8Proxy::DOMImplementationToV8Object(
3265 DOMImplementation* impl)
3266 {
3267 v8::Handle<v8::Object> result =
3268 InstantiateV8Object(V8ClassIndex::DOMIMPLEMENTATION,
3269 V8ClassIndex::DOMIMPLEMENTATION,
3270 impl);
3271 if (result.IsEmpty()) {
3272 // If the instantiation failed, we ignore it and return null instead
3273 // of returning an empty handle.
3274 return v8::Null();
3275 }
3276 return result;
3277 }
3278
3279
3280 v8::Handle<v8::Value> V8Proxy::StyleSheetToV8Object(StyleSheet* sheet)
3281 {
3282 if (!sheet) return v8::Null();
3283
3284 v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(sheet);
3285 if (!wrapper.IsEmpty())
3286 return wrapper;
3287
3288 V8ClassIndex::V8WrapperType type = V8ClassIndex::STYLESHEET;
3289 if (sheet->isCSSStyleSheet())
3290 type = V8ClassIndex::CSSSTYLESHEET;
3291
3292 v8::Handle<v8::Object> result =
3293 InstantiateV8Object(type, V8ClassIndex::STYLESHEET, sheet);
3294 if (!result.IsEmpty()) {
3295 // Only update the DOM object map if the result is non-empty.
3296 sheet->ref();
3297 SetJSWrapperForDOMObject(sheet, v8::Persistent<v8::Object>::New(result));
3298 }
3299
3300 // Add a hidden reference from stylesheet object to its owner node.
3301 Node* owner_node = sheet->ownerNode();
3302 if (owner_node) {
3303 v8::Handle<v8::Object> owner =
3304 v8::Handle<v8::Object>::Cast(NodeToV8Object(owner_node));
3305 result->SetInternalField(V8Custom::kStyleSheetOwnerNodeIndex, owner);
3306 }
3307
3308 return result;
3309 }
3310
3311
3312 v8::Handle<v8::Value> V8Proxy::CSSValueToV8Object(CSSValue* value)
3313 {
3314 if (!value) return v8::Null();
3315
3316 v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(value);
3317 if (!wrapper.IsEmpty())
3318 return wrapper;
3319
3320 V8ClassIndex::V8WrapperType type;
3321
3322 if (value->isWebKitCSSTransformValue())
3323 type = V8ClassIndex::WEBKITCSSTRANSFORMVALUE;
3324 else if (value->isValueList())
3325 type = V8ClassIndex::CSSVALUELIST;
3326 else if (value->isPrimitiveValue())
3327 type = V8ClassIndex::CSSPRIMITIVEVALUE;
3328 #if ENABLE(SVG)
3329 else if (value->isSVGPaint())
3330 type = V8ClassIndex::SVGPAINT;
3331 else if (value->isSVGColor())
3332 type = V8ClassIndex::SVGCOLOR;
3333 #endif
3334 else
3335 type = V8ClassIndex::CSSVALUE;
3336
3337 v8::Handle<v8::Object> result =
3338 InstantiateV8Object(type, V8ClassIndex::CSSVALUE, value);
3339 if (!result.IsEmpty()) {
3340 // Only update the DOM object map if the result is non-empty.
3341 value->ref();
3342 SetJSWrapperForDOMObject(value, v8::Persistent<v8::Object>::New(result));
3343 }
3344
3345 return result;
3346 }
3347
3348
3349 v8::Handle<v8::Value> V8Proxy::CSSRuleToV8Object(CSSRule* rule)
3350 {
3351 if (!rule) return v8::Null();
3352
3353 v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(rule);
3354 if (!wrapper.IsEmpty())
3355 return wrapper;
3356
3357 V8ClassIndex::V8WrapperType type;
3358
3359 switch (rule->type()) {
3360 case CSSRule::STYLE_RULE:
3361 type = V8ClassIndex::CSSSTYLERULE;
3362 break;
3363 case CSSRule::CHARSET_RULE:
3364 type = V8ClassIndex::CSSCHARSETRULE;
3365 break;
3366 case CSSRule::IMPORT_RULE:
3367 type = V8ClassIndex::CSSIMPORTRULE;
3368 break;
3369 case CSSRule::MEDIA_RULE:
3370 type = V8ClassIndex::CSSMEDIARULE;
3371 break;
3372 case CSSRule::FONT_FACE_RULE:
3373 type = V8ClassIndex::CSSFONTFACERULE;
3374 break;
3375 case CSSRule::PAGE_RULE:
3376 type = V8ClassIndex::CSSPAGERULE;
3377 break;
3378 case CSSRule::VARIABLES_RULE:
3379 type = V8ClassIndex::CSSVARIABLESRULE;
3380 break;
3381 case CSSRule::WEBKIT_KEYFRAME_RULE:
3382 type = V8ClassIndex::WEBKITCSSKEYFRAMERULE;
3383 break;
3384 case CSSRule::WEBKIT_KEYFRAMES_RULE:
3385 type = V8ClassIndex::WEBKITCSSKEYFRAMESRULE;
3386 break;
3387 default: // CSSRule::UNKNOWN_RULE
3388 type = V8ClassIndex::CSSRULE;
3389 break;
3390 }
3391
3392 v8::Handle<v8::Object> result =
3393 InstantiateV8Object(type, V8ClassIndex::CSSRULE, rule);
3394 if (!result.IsEmpty()) {
3395 // Only update the DOM object map if the result is non-empty.
3396 rule->ref();
3397 SetJSWrapperForDOMObject(rule, v8::Persistent<v8::Object>::New(result));
3398 }
3399 return result;
3400 }
3401
3402 v8::Handle<v8::Value> V8Proxy::WindowToV8Object(DOMWindow* window)
3403 {
3404 if (!window) return v8::Null();
3405 // Initializes environment of a frame, and return the global object
3406 // of the frame.
3407 Frame* frame = window->frame();
3408 if (!frame)
3409 return v8::Handle<v8::Object>();
3410
3411 // Special case: Because of evaluateInNewContext() one DOMWindow can have
3412 // multiple contexts and multiple global objects associated with it. When
3413 // code running in one of those contexts accesses the window object, we
3414 // want to return the global object associated with that context, not
3415 // necessarily the first global object associated with that DOMWindow.
3416 v8::Handle<v8::Context> current_context = v8::Context::GetCurrent();
3417 v8::Handle<v8::Object> current_global = current_context->Global();
3418 v8::Handle<v8::Object> windowWrapper =
3419 LookupDOMWrapper(V8ClassIndex::DOMWINDOW, current_global);
3420 if (!windowWrapper.IsEmpty())
3421 if (DOMWrapperToNative<DOMWindow>(windowWrapper) == window)
3422 return current_global;
3423
3424 // Otherwise, return the global object associated with this frame.
3425 v8::Handle<v8::Context> context = GetContext(frame);
3426 if (context.IsEmpty())
3427 return v8::Handle<v8::Object>();
3428
3429 v8::Handle<v8::Object> global = context->Global();
3430 ASSERT(!global.IsEmpty());
3431 return global;
3432 }
3433
3434 void V8Proxy::BindJSObjectToWindow(Frame* frame,
3435 const char* name,
3436 int type,
3437 v8::Handle<v8::FunctionTemplate> desc,
3438 void* imp)
3439 {
3440 // Get environment.
3441 v8::Handle<v8::Context> context = V8Proxy::GetContext(frame);
3442 if (context.IsEmpty())
3443 return; // JS not enabled.
3444
3445 v8::Context::Scope scope(context);
3446 v8::Handle<v8::Object> instance = desc->GetFunction();
3447 SetDOMWrapper(instance, type, imp);
3448
3449 v8::Handle<v8::Object> global = context->Global();
3450 global->Set(v8::String::New(name), instance);
3451 }
3452
3453 void V8Proxy::ProcessConsoleMessages()
3454 {
3455 ConsoleMessageManager::ProcessDelayedMessages();
3456 }
3457
3458
3459 // Create the utility context for holding JavaScript functions used internally
3460 // which are not visible to JavaScript executing on the page.
3461 void V8Proxy::CreateUtilityContext() {
3462 ASSERT(m_utilityContext.IsEmpty());
3463
3464 v8::HandleScope scope;
3465 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
3466 m_utilityContext = v8::Context::New(NULL, global_template);
3467 v8::Context::Scope context_scope(m_utilityContext);
3468
3469 // Compile JavaScript function for retrieving the source line of the top
3470 // JavaScript stack frame.
3471 static const char* frame_source_line_source =
3472 "function frame_source_line(exec_state) {"
3473 " return exec_state.frame(0).sourceLine();"
3474 "}";
3475 v8::Script::Compile(v8::String::New(frame_source_line_source))->Run();
3476
3477 // Compile JavaScript function for retrieving the source name of the top
3478 // JavaScript stack frame.
3479 static const char* frame_source_name_source =
3480 "function frame_source_name(exec_state) {"
3481 " var frame = exec_state.frame(0);"
3482 " if (frame.func().resolved() && "
3483 " frame.func().script() && "
3484 " frame.func().script().name()) {"
3485 " return frame.func().script().name();"
3486 " }"
3487 "}";
3488 v8::Script::Compile(v8::String::New(frame_source_name_source))->Run();
3489 }
3490
3491
3492 int V8Proxy::GetSourceLineNumber() {
3493 v8::HandleScope scope;
3494 v8::Handle<v8::Context> utility_context = V8Proxy::GetUtilityContext();
3495 if (utility_context.IsEmpty()) {
3496 return 0;
3497 }
3498 v8::Context::Scope context_scope(utility_context);
3499 v8::Handle<v8::Function> frame_source_line;
3500 frame_source_line = v8::Local<v8::Function>::Cast(
3501 utility_context->Global()->Get(v8::String::New("frame_source_line")));
3502 if (frame_source_line.IsEmpty()) {
3503 return 0;
3504 }
3505 v8::Handle<v8::Value> result = v8::Debug::Call(frame_source_line);
3506 if (result.IsEmpty()) {
3507 return 0;
3508 }
3509 return result->Int32Value();
3510 }
3511
3512
3513 String V8Proxy::GetSourceName() {
3514 v8::HandleScope scope;
3515 v8::Handle<v8::Context> utility_context = GetUtilityContext();
3516 if (utility_context.IsEmpty()) {
3517 return String();
3518 }
3519 v8::Context::Scope context_scope(utility_context);
3520 v8::Handle<v8::Function> frame_source_name;
3521 frame_source_name = v8::Local<v8::Function>::Cast(
3522 utility_context->Global()->Get(v8::String::New("frame_source_name")));
3523 if (frame_source_name.IsEmpty()) {
3524 return String();
3525 }
3526 return ToWebCoreString(v8::Debug::Call(frame_source_name));
3527 }
3528
3529 void V8Proxy::RegisterExtension(v8::Extension* extension,
3530 const String& schemeRestriction) {
3531 v8::RegisterExtension(extension);
3532 V8ExtensionInfo info = {schemeRestriction, extension};
3533 m_extensions.push_back(info);
3534 }
3535
3536 bool V8Proxy::SetContextDebugId(int debug_id) {
3537 ASSERT(debug_id > 0);
3538 if (m_context.IsEmpty()) {
3539 return false;
3540 }
3541 v8::HandleScope scope;
3542 if (!m_context->GetData()->IsUndefined()) {
3543 return false;
3544 }
3545
3546 v8::Handle<v8::Object> context_data = v8::Object::New();
3547 context_data->Set(v8::String::New(kContextDebugDataType),
3548 v8::String::New("page"));
3549 context_data->Set(v8::String::New(kContextDebugDataValue),
3550 v8::Integer::New(debug_id));
3551 m_context->SetData(context_data);
3552 return true;
3553 }
3554
3555 // static
3556 int V8Proxy::GetContextDebugId(v8::Handle<v8::Context> context) {
3557 v8::HandleScope scope;
3558 if (!context->GetData()->IsObject()) {
3559 return -1;
3560 }
3561 v8::Handle<v8::Value> data = context->GetData()->ToObject()->Get(
3562 v8::String::New(kContextDebugDataValue));
3563 return data->IsInt32() ? data->Int32Value() : -1;
3564 }
3565
3566 } // namespace WebCore
OLDNEW
« no previous file with comments | « webkit/port/bindings/v8/v8_proxy.h ('k') | webkit/port/bindings/v8/v8_utility.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698