OLD | NEW |
| (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 | |
OLD | NEW |