2 * Copyright (C) 2009, 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "V8DOMWindow.h"
34 #include "BindingState.h"
36 #include "ContentSecurityPolicy.h"
38 #include "DOMWindow.h"
39 #include "ExceptionCode.h"
41 #include "FrameLoadRequest.h"
42 #include "FrameLoader.h"
43 #include "FrameView.h"
44 #include "HTMLCollection.h"
45 #include "HTMLDocument.h"
47 #include "MediaPlayer.h"
48 #include "MessagePort.h"
50 #include "PlatformScreen.h"
51 #include "ScheduledAction.h"
52 #include "ScriptCallStack.h"
53 #include "ScriptSourceCode.h"
54 #include "SerializedScriptValue.h"
56 #include "SharedWorkerRepository.h"
58 #include "V8Binding.h"
59 #include "V8EventListener.h"
60 #include "V8EventListenerList.h"
61 #include "V8GCForContextDispose.h"
62 #include "V8HiddenPropertyName.h"
63 #include "V8HTMLCollection.h"
65 #include "V8Utilities.h"
66 #include "WindowFeatures.h"
67 #include <wtf/ArrayBuffer.h>
71 v8::Handle<v8::Value> WindowSetTimeoutImpl(const v8::Arguments& args, bool singleShot)
73 int argumentCount = args.Length();
75 if (argumentCount < 1)
76 return v8::Undefined();
78 DOMWindow* imp = V8DOMWindow::toNative(args.Holder());
79 ScriptExecutionContext* scriptContext = static_cast<ScriptExecutionContext*>(imp->document());
82 return setDOMException(INVALID_ACCESS_ERR, args.GetIsolate());
84 v8::Handle<v8::Value> function = args[0];
85 WTF::String functionString;
86 if (!function->IsFunction()) {
87 if (function->IsString())
88 functionString = toWebCoreString(function);
90 v8::Handle<v8::Value> v8String = function->ToString();
92 // Bail out if string conversion failed.
93 if (v8String.IsEmpty())
94 return v8::Undefined();
96 functionString = toWebCoreString(v8String);
99 // Don't allow setting timeouts to run empty functions!
101 if (functionString.length() == 0)
102 return v8::Undefined();
106 if (argumentCount >= 2)
107 timeout = args[1]->Int32Value();
109 if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame()))
110 return v8::Undefined();
113 if (function->IsFunction()) {
114 int paramCount = argumentCount >= 2 ? argumentCount - 2 : 0;
115 v8::Local<v8::Value>* params = 0;
116 if (paramCount > 0) {
117 params = new v8::Local<v8::Value>[paramCount];
118 for (int i = 0; i < paramCount; i++)
119 // parameters must be globalized
120 params[i] = args[i+2];
123 // params is passed to action, and released in action's destructor
124 ASSERT(imp->frame());
125 OwnPtr<ScheduledAction> action = adoptPtr(new ScheduledAction(imp->frame()->script()->currentWorldContext(), v8::Handle<v8::Function>::Cast(function), paramCount, params, args.GetIsolate()));
127 // FIXME: We should use OwnArrayPtr for params.
130 id = DOMTimer::install(scriptContext, action.release(), timeout, singleShot);
132 if (imp->document() && !imp->document()->contentSecurityPolicy()->allowEval())
133 return v8Integer(0, args.GetIsolate());
134 ASSERT(imp->frame());
135 id = DOMTimer::install(scriptContext, adoptPtr(new ScheduledAction(imp->frame()->script()->currentWorldContext(), functionString, KURL(), args.GetIsolate())), timeout, singleShot);
138 // Try to do the idle notification before the timeout expires to get better
139 // use of any idle time. Aim for the middle of the interval for simplicity.
141 double maximumFireInterval = static_cast<double>(timeout) / 1000 / 2;
142 V8GCForContextDispose::instance().notifyIdleSooner(maximumFireInterval);
145 return v8Integer(id, args.GetIsolate());
148 v8::Handle<v8::Value> V8DOMWindow::eventAttrGetterCustom(v8::Local<v8::String> name, const v8::AccessorInfo& info)
150 v8::Handle<v8::Object> holder = info.This()->FindInstanceInPrototypeChain(V8DOMWindow::GetTemplate(info.GetIsolate(), worldTypeInMainThread(info.GetIsolate())));
151 if (holder.IsEmpty())
152 return v8::Undefined();
154 Frame* frame = V8DOMWindow::toNative(holder)->frame();
155 if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), frame))
156 return v8::Undefined();
159 v8::Local<v8::Context> context = frame->script()->currentWorldContext();
160 if (context.IsEmpty())
161 return v8::Undefined();
163 v8::Handle<v8::String> eventSymbol = V8HiddenPropertyName::event();
164 v8::Handle<v8::Value> jsEvent = context->Global()->GetHiddenValue(eventSymbol);
165 if (jsEvent.IsEmpty())
166 return v8::Undefined();
170 void V8DOMWindow::eventAttrSetterCustom(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
172 v8::Handle<v8::Object> holder = info.This()->FindInstanceInPrototypeChain(V8DOMWindow::GetTemplate(info.GetIsolate(), worldTypeInMainThread(info.GetIsolate())));
173 if (holder.IsEmpty())
176 Frame* frame = V8DOMWindow::toNative(holder)->frame();
177 if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), frame))
181 v8::Local<v8::Context> context = frame->script()->currentWorldContext();
182 if (context.IsEmpty())
185 v8::Handle<v8::String> eventSymbol = V8HiddenPropertyName::event();
186 context->Global()->SetHiddenValue(eventSymbol, value);
189 void V8DOMWindow::locationAttrSetterCustom(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
191 DOMWindow* imp = V8DOMWindow::toNative(info.Holder());
192 BindingState* state = BindingState::instance();
194 DOMWindow* active = activeDOMWindow(state);
198 DOMWindow* first = firstDOMWindow(state);
202 if (Location* location = imp->location())
203 location->setHref(toWebCoreString(value), active, first);
206 void V8DOMWindow::openerAttrSetterCustom(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
208 DOMWindow* imp = V8DOMWindow::toNative(info.Holder());
210 if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame()))
213 // Opener can be shadowed if it is in the same domain.
214 // Have a special handling of null value to behave
215 // like Firefox. See bug http://b/1224887 & http://b/791706.
216 if (value->IsNull()) {
217 // imp->frame() cannot be null,
218 // otherwise, SameOrigin check would have failed.
219 ASSERT(imp->frame());
220 imp->frame()->loader()->setOpener(0);
223 // Delete the accessor from this object.
224 info.Holder()->Delete(name);
226 // Put property on the front (this) object.
227 info.This()->Set(name, value);
230 v8::Handle<v8::Value> V8DOMWindow::addEventListenerMethodCustom(const v8::Arguments& args)
232 String eventType = toWebCoreString(args[0]);
233 bool useCapture = args[2]->BooleanValue();
235 DOMWindow* imp = V8DOMWindow::toNative(args.Holder());
237 if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame()))
238 return v8::Undefined();
240 Document* doc = imp->document();
243 return v8::Undefined();
245 // FIXME: Check if there is not enough arguments
247 return v8::Undefined();
249 RefPtr<EventListener> listener = V8EventListenerList::getEventListener(args[1], false, ListenerFindOrCreate);
252 imp->addEventListener(eventType, listener, useCapture);
253 createHiddenDependency(args.Holder(), args[1], eventListenerCacheIndex, args.GetIsolate());
256 return v8::Undefined();
260 v8::Handle<v8::Value> V8DOMWindow::removeEventListenerMethodCustom(const v8::Arguments& args)
262 String eventType = toWebCoreString(args[0]);
263 bool useCapture = args[2]->BooleanValue();
265 DOMWindow* imp = V8DOMWindow::toNative(args.Holder());
267 if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame()))
268 return v8::Undefined();
270 Document* doc = imp->document();
273 return v8::Undefined();
276 return v8::Undefined();
278 RefPtr<EventListener> listener = V8EventListenerList::getEventListener(args[1], false, ListenerFindOnly);
281 imp->removeEventListener(eventType, listener.get(), useCapture);
282 removeHiddenDependency(args.Holder(), args[1], eventListenerCacheIndex, args.GetIsolate());
285 return v8::Undefined();
288 static bool isLegacyTargetOriginDesignation(v8::Handle<v8::Value> value)
290 if (value->IsString() || value->IsStringObject())
296 v8::Handle<v8::Value> V8DOMWindow::postMessageMethodCustom(const v8::Arguments& args)
298 // None of these need to be RefPtr because args and context are guaranteed
299 // to hold on to them.
300 DOMWindow* window = V8DOMWindow::toNative(args.Holder());
301 DOMWindow* source = activeDOMWindow(BindingState::instance());
303 // If called directly by WebCore we don't have a calling context.
305 return throwTypeError(0, args.GetIsolate());
307 // This function has variable arguments and can be:
309 // postMessage(message, targetOrigin)
310 // postMessage(message, targetOrigin, {sequence of transferrables})
311 // Legacy non-standard implementations in webkit allowed:
312 // postMessage(message, {sequence of transferrables}, targetOrigin);
313 MessagePortArray portArray;
314 ArrayBufferArray arrayBufferArray;
315 int targetOriginArgIndex = 1;
316 if (args.Length() > 2) {
317 int transferablesArgIndex = 2;
318 if (isLegacyTargetOriginDesignation(args[2])) {
319 targetOriginArgIndex = 2;
320 transferablesArgIndex = 1;
322 if (!extractTransferables(args[transferablesArgIndex], portArray, arrayBufferArray, args.GetIsolate()))
323 return v8::Undefined();
325 V8TRYCATCH_FOR_V8STRINGRESOURCE(V8StringResource<WithUndefinedOrNullCheck>, targetOrigin, args[targetOriginArgIndex]);
327 bool didThrow = false;
328 RefPtr<SerializedScriptValue> message =
329 SerializedScriptValue::create(args[0],
335 return v8::Undefined();
337 ExceptionCode ec = 0;
338 window->postMessage(message.release(), &portArray, targetOrigin, source, ec);
339 return setDOMException(ec, args.GetIsolate());
342 // FIXME(fqian): returning string is cheating, and we should
343 // fix this by calling toString function on the receiver.
344 // However, V8 implements toString in JavaScript, which requires
345 // switching context of receiver. I consider it is dangerous.
346 v8::Handle<v8::Value> V8DOMWindow::toStringMethodCustom(const v8::Arguments& args)
348 v8::Handle<v8::Object> domWrapper = args.This()->FindInstanceInPrototypeChain(V8DOMWindow::GetTemplate(args.GetIsolate(), worldTypeInMainThread(args.GetIsolate())));
349 if (domWrapper.IsEmpty())
350 return args.This()->ObjectProtoToString();
351 return domWrapper->ObjectProtoToString();
354 v8::Handle<v8::Value> V8DOMWindow::releaseEventsMethodCustom(const v8::Arguments& args)
356 return v8::Undefined();
359 v8::Handle<v8::Value> V8DOMWindow::captureEventsMethodCustom(const v8::Arguments& args)
361 return v8::Undefined();
364 class DialogHandler {
366 explicit DialogHandler(v8::Handle<v8::Value> dialogArguments)
367 : m_dialogArguments(dialogArguments)
371 void dialogCreated(DOMWindow*);
372 v8::Handle<v8::Value> returnValue() const;
375 v8::Handle<v8::Value> m_dialogArguments;
376 v8::Handle<v8::Context> m_dialogContext;
379 inline void DialogHandler::dialogCreated(DOMWindow* dialogFrame)
381 m_dialogContext = dialogFrame->frame() ? dialogFrame->frame()->script()->currentWorldContext() : v8::Local<v8::Context>();
382 if (m_dialogContext.IsEmpty())
384 if (m_dialogArguments.IsEmpty())
386 v8::Context::Scope scope(m_dialogContext);
387 m_dialogContext->Global()->Set(v8::String::NewSymbol("dialogArguments"), m_dialogArguments);
390 inline v8::Handle<v8::Value> DialogHandler::returnValue() const
392 if (m_dialogContext.IsEmpty())
393 return v8::Undefined();
394 v8::Context::Scope scope(m_dialogContext);
395 v8::Handle<v8::Value> returnValue = m_dialogContext->Global()->Get(v8::String::NewSymbol("returnValue"));
396 if (returnValue.IsEmpty())
397 return v8::Undefined();
401 static void setUpDialog(DOMWindow* dialog, void* handler)
403 static_cast<DialogHandler*>(handler)->dialogCreated(dialog);
406 v8::Handle<v8::Value> V8DOMWindow::showModalDialogMethodCustom(const v8::Arguments& args)
408 DOMWindow* impl = V8DOMWindow::toNative(args.Holder());
409 BindingState* state = BindingState::instance();
410 if (!BindingSecurity::shouldAllowAccessToFrame(state, impl->frame()))
411 return v8::Undefined();
413 // FIXME: Handle exceptions properly.
414 String urlString = toWebCoreStringWithUndefinedOrNullCheck(args[0]);
415 DialogHandler handler(args[1]);
416 String dialogFeaturesString = toWebCoreStringWithUndefinedOrNullCheck(args[2]);
418 impl->showModalDialog(urlString, dialogFeaturesString, activeDOMWindow(state), firstDOMWindow(state), setUpDialog, &handler);
420 return handler.returnValue();
423 v8::Handle<v8::Value> V8DOMWindow::openMethodCustom(const v8::Arguments& args)
425 DOMWindow* impl = V8DOMWindow::toNative(args.Holder());
426 BindingState* state = BindingState::instance();
427 if (!BindingSecurity::shouldAllowAccessToFrame(state, impl->frame()))
428 return v8::Undefined();
430 // FIXME: Handle exceptions properly.
431 String urlString = toWebCoreStringWithUndefinedOrNullCheck(args[0]);
432 AtomicString frameName = (args[1]->IsUndefined() || args[1]->IsNull()) ? "_blank" : AtomicString(toWebCoreString(args[1]));
433 String windowFeaturesString = toWebCoreStringWithUndefinedOrNullCheck(args[2]);
435 RefPtr<DOMWindow> openedWindow = impl->open(urlString, frameName, windowFeaturesString, activeDOMWindow(state), firstDOMWindow(state));
437 return v8::Undefined();
439 return toV8Fast(openedWindow.release(), args, impl);
442 v8::Handle<v8::Value> V8DOMWindow::indexedPropertyGetter(uint32_t index, const v8::AccessorInfo& info)
445 DOMWindow* window = V8DOMWindow::toNative(info.Holder());
447 return v8Undefined();
449 Frame* frame = window->frame();
451 return v8Undefined();
453 Frame* child = frame->tree()->scopedChild(index);
455 return toV8Fast(child->document()->domWindow(), info, window);
457 return v8Undefined();
460 v8::Handle<v8::Value> V8DOMWindow::namedPropertyGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
463 DOMWindow* window = V8DOMWindow::toNative(info.Holder());
465 return v8Undefined();
467 Frame* frame = window->frame();
468 // window is detached from a frame.
470 return v8Undefined();
472 // Search sub-frames.
473 AtomicString propName = toWebCoreAtomicString(name);
474 Frame* child = frame->tree()->scopedChild(propName);
476 return toV8Fast(child->document()->domWindow(), info, window);
478 // Search IDL functions defined in the prototype
479 if (!info.Holder()->GetRealNamedProperty(name).IsEmpty())
480 return v8Undefined();
482 // Search named items in the document.
483 Document* doc = frame->document();
485 if (doc && doc->isHTMLDocument()) {
486 if (static_cast<HTMLDocument*>(doc)->hasNamedItem(propName.impl()) || doc->hasElementWithId(propName.impl())) {
487 RefPtr<HTMLCollection> items = doc->windowNamedItems(propName);
488 if (!items->isEmpty()) {
489 if (items->hasExactlyOneItem())
490 return toV8Fast(items->item(0), info, window);
491 return toV8Fast(items.release(), info, window);
496 return v8Undefined();
500 v8::Handle<v8::Value> V8DOMWindow::setTimeoutMethodCustom(const v8::Arguments& args)
502 return WindowSetTimeoutImpl(args, true);
506 v8::Handle<v8::Value> V8DOMWindow::setIntervalMethodCustom(const v8::Arguments& args)
508 return WindowSetTimeoutImpl(args, false);
511 bool V8DOMWindow::namedSecurityCheckCustom(v8::Local<v8::Object> host, v8::Local<v8::Value> key, v8::AccessType type, v8::Local<v8::Value>)
513 v8::Isolate* isolate = v8::Isolate::GetCurrent();
514 v8::Handle<v8::Object> window = host->FindInstanceInPrototypeChain(V8DOMWindow::GetTemplate(isolate, worldTypeInMainThread(isolate)));
515 if (window.IsEmpty())
516 return false; // the frame is gone.
518 DOMWindow* targetWindow = V8DOMWindow::toNative(window);
520 ASSERT(targetWindow);
522 Frame* target = targetWindow->frame();
526 if (key->IsString()) {
527 DEFINE_STATIC_LOCAL(AtomicString, nameOfProtoProperty, ("__proto__", AtomicString::ConstructFromLiteral));
529 String name = toWebCoreString(key);
530 Frame* childFrame = target->tree()->scopedChild(name);
531 // Notice that we can't call HasRealNamedProperty for ACCESS_HAS
532 // because that would generate infinite recursion.
533 if (type == v8::ACCESS_HAS && childFrame)
535 // We need to explicitly compare against nameOfProtoProperty because
536 // V8's JSObject::LocalLookup finds __proto__ before
537 // interceptors and even when __proto__ isn't a "real named property".
538 if (type == v8::ACCESS_GET && childFrame && !host->HasRealNamedProperty(key->ToString()) && name != nameOfProtoProperty)
542 return BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), target, DoNotReportSecurityError);
545 bool V8DOMWindow::indexedSecurityCheckCustom(v8::Local<v8::Object> host, uint32_t index, v8::AccessType type, v8::Local<v8::Value>)
547 v8::Isolate* isolate = v8::Isolate::GetCurrent();
548 v8::Handle<v8::Object> window = host->FindInstanceInPrototypeChain(V8DOMWindow::GetTemplate(isolate, worldTypeInMainThread(isolate)));
549 if (window.IsEmpty())
552 DOMWindow* targetWindow = V8DOMWindow::toNative(window);
554 ASSERT(targetWindow);
556 Frame* target = targetWindow->frame();
559 Frame* childFrame = target->tree()->scopedChild(index);
561 // Notice that we can't call HasRealNamedProperty for ACCESS_HAS
562 // because that would generate infinite recursion.
563 if (type == v8::ACCESS_HAS && childFrame)
565 if (type == v8::ACCESS_GET && childFrame && !host->HasRealIndexedProperty(index))
568 return BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), target, DoNotReportSecurityError);
571 v8::Handle<v8::Value> toV8(DOMWindow* window, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
573 // Notice that we explicitly ignore creationContext because the DOMWindow is its own creationContext.
576 return v8NullWithCheck(isolate);
577 // Initializes environment of a frame, and return the global object
579 Frame* frame = window->frame();
581 return v8Undefined();
583 // Special case: Because of evaluateInIsolatedWorld() one DOMWindow can have
584 // multiple contexts and multiple global objects associated with it. When
585 // code running in one of those contexts accesses the window object, we
586 // want to return the global object associated with that context, not
587 // necessarily the first global object associated with that DOMWindow.
588 v8::Handle<v8::Context> currentContext = v8::Context::GetCurrent();
589 v8::Handle<v8::Object> currentGlobal = currentContext->Global();
590 v8::Handle<v8::Object> windowWrapper = currentGlobal->FindInstanceInPrototypeChain(V8DOMWindow::GetTemplate(isolate, worldTypeInMainThread(isolate)));
591 if (!windowWrapper.IsEmpty()) {
592 if (V8DOMWindow::toNative(windowWrapper) == window)
593 return currentGlobal;
596 // Otherwise, return the global object associated with this frame.
597 v8::Handle<v8::Context> context = frame->script()->currentWorldContext();
598 if (context.IsEmpty())
599 return v8Undefined();
601 v8::Handle<v8::Object> global = context->Global();
602 ASSERT(!global.IsEmpty());
606 } // namespace WebCore