V8DOMWindowCustom.cpp [plain text]
#include "config.h"
#include "V8DOMWindow.h"
#include <wtf/ArrayBuffer.h>
#include "Chrome.h"
#include "ContentSecurityPolicy.h"
#include "DOMTimer.h"
#include "DOMWindow.h"
#include "ExceptionCode.h"
#include "Frame.h"
#include "FrameLoadRequest.h"
#include "FrameView.h"
#include "HTMLCollection.h"
#include "HTMLDocument.h"
#include "Location.h"
#include "MediaPlayer.h"
#include "MessagePort.h"
#include "Page.h"
#include "PlatformScreen.h"
#include "ScheduledAction.h"
#include "ScriptSourceCode.h"
#include "SerializedScriptValue.h"
#include "Settings.h"
#include "SharedWorkerRepository.h"
#include "Storage.h"
#include "V8Binding.h"
#include "V8BindingMacros.h"
#include "V8BindingState.h"
#include "V8EventListener.h"
#include "V8GCForContextDispose.h"
#include "V8HiddenPropertyName.h"
#include "V8HTMLCollection.h"
#include "V8Node.h"
#include "V8Proxy.h"
#include "V8Utilities.h"
#include "WindowFeatures.h"
namespace WebCore {
v8::Handle<v8::Value> WindowSetTimeoutImpl(const v8::Arguments& args, bool singleShot)
{
int argumentCount = args.Length();
if (argumentCount < 1)
return v8::Undefined();
DOMWindow* imp = V8DOMWindow::toNative(args.Holder());
ScriptExecutionContext* scriptContext = static_cast<ScriptExecutionContext*>(imp->document());
if (!scriptContext) {
V8Proxy::setDOMException(INVALID_ACCESS_ERR, args.GetIsolate());
return v8::Undefined();
}
v8::Handle<v8::Value> function = args[0];
WTF::String functionString;
if (!function->IsFunction()) {
if (function->IsString())
functionString = toWebCoreString(function);
else {
v8::Handle<v8::Value> v8String = function->ToString();
if (v8String.IsEmpty())
return v8::Undefined();
functionString = toWebCoreString(v8String);
}
if (functionString.length() == 0)
return v8::Undefined();
}
int32_t timeout = 0;
if (argumentCount >= 2)
timeout = args[1]->Int32Value();
if (!V8BindingSecurity::canAccessFrame(V8BindingState::Only(), imp->frame(), true))
return v8::Undefined();
int id;
if (function->IsFunction()) {
int paramCount = argumentCount >= 2 ? argumentCount - 2 : 0;
v8::Local<v8::Value>* params = 0;
if (paramCount > 0) {
params = new v8::Local<v8::Value>[paramCount];
for (int i = 0; i < paramCount; i++)
params[i] = args[i+2];
}
OwnPtr<ScheduledAction> action = adoptPtr(new ScheduledAction(V8Proxy::context(imp->frame()), v8::Handle<v8::Function>::Cast(function), paramCount, params));
delete[] params;
id = DOMTimer::install(scriptContext, action.release(), timeout, singleShot);
} else {
if (imp->document() && !imp->document()->contentSecurityPolicy()->allowEval())
return v8::Integer::New(0);
id = DOMTimer::install(scriptContext, adoptPtr(new ScheduledAction(V8Proxy::context(imp->frame()), functionString)), timeout, singleShot);
}
if (timeout > 0) {
double maximumFireInterval = static_cast<double>(timeout) / 1000 / 2;
V8GCForContextDispose::instance().notifyIdleSooner(maximumFireInterval);
}
return v8::Integer::New(id);
}
v8::Handle<v8::Value> V8DOMWindow::eventAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
{
v8::Handle<v8::Object> holder = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), info.This());
if (holder.IsEmpty())
return v8::Undefined();
Frame* frame = V8DOMWindow::toNative(holder)->frame();
if (!V8BindingSecurity::canAccessFrame(V8BindingState::Only(), frame, true))
return v8::Undefined();
v8::Local<v8::Context> context = V8Proxy::context(frame);
if (context.IsEmpty())
return v8::Undefined();
v8::Handle<v8::String> eventSymbol = V8HiddenPropertyName::event();
v8::Handle<v8::Value> jsEvent = context->Global()->GetHiddenValue(eventSymbol);
if (jsEvent.IsEmpty())
return v8::Undefined();
return jsEvent;
}
void V8DOMWindow::eventAccessorSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
{
v8::Handle<v8::Object> holder = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), info.This());
if (holder.IsEmpty())
return;
Frame* frame = V8DOMWindow::toNative(holder)->frame();
if (!V8BindingSecurity::canAccessFrame(V8BindingState::Only(), frame, true))
return;
v8::Local<v8::Context> context = V8Proxy::context(frame);
if (context.IsEmpty())
return;
v8::Handle<v8::String> eventSymbol = V8HiddenPropertyName::event();
context->Global()->SetHiddenValue(eventSymbol, value);
}
void V8DOMWindow::locationAccessorSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
{
DOMWindow* imp = V8DOMWindow::toNative(info.Holder());
State<V8Binding>* state = V8BindingState::Only();
DOMWindow* activeWindow = state->activeWindow();
if (!activeWindow)
return;
DOMWindow* firstWindow = state->firstWindow();
if (!firstWindow)
return;
if (Location* location = imp->location())
location->setHref(toWebCoreString(value), activeWindow, firstWindow);
}
void V8DOMWindow::openerAccessorSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
{
DOMWindow* imp = V8DOMWindow::toNative(info.Holder());
if (!V8BindingSecurity::canAccessFrame(V8BindingState::Only(), imp->frame(), true))
return;
if (value->IsNull()) {
ASSERT(imp->frame());
imp->frame()->loader()->setOpener(0);
}
info.Holder()->Delete(name);
info.This()->Set(name, value);
}
v8::Handle<v8::Value> V8DOMWindow::addEventListenerCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.addEventListener()");
String eventType = toWebCoreString(args[0]);
bool useCapture = args[2]->BooleanValue();
DOMWindow* imp = V8DOMWindow::toNative(args.Holder());
if (!V8BindingSecurity::canAccessFrame(V8BindingState::Only(), imp->frame(), true))
return v8::Undefined();
Document* doc = imp->document();
if (!doc)
return v8::Undefined();
V8Proxy* proxy = V8Proxy::retrieve(imp->frame());
if (!proxy)
return v8::Undefined();
RefPtr<EventListener> listener = V8DOMWrapper::getEventListener(args[1], false, ListenerFindOrCreate);
if (listener) {
imp->addEventListener(eventType, listener, useCapture);
createHiddenDependency(args.Holder(), args[1], eventListenerCacheIndex);
}
return v8::Undefined();
}
v8::Handle<v8::Value> V8DOMWindow::removeEventListenerCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.removeEventListener()");
String eventType = toWebCoreString(args[0]);
bool useCapture = args[2]->BooleanValue();
DOMWindow* imp = V8DOMWindow::toNative(args.Holder());
if (!V8BindingSecurity::canAccessFrame(V8BindingState::Only(), imp->frame(), true))
return v8::Undefined();
Document* doc = imp->document();
if (!doc)
return v8::Undefined();
V8Proxy* proxy = V8Proxy::retrieve(imp->frame());
if (!proxy)
return v8::Undefined();
RefPtr<EventListener> listener = V8DOMWrapper::getEventListener(args[1], false, ListenerFindOnly);
if (listener) {
imp->removeEventListener(eventType, listener.get(), useCapture);
removeHiddenDependency(args.Holder(), args[1], eventListenerCacheIndex);
}
return v8::Undefined();
}
static bool isLegacyTargetOriginDesignation(v8::Handle<v8::Value> value)
{
if (value->IsString() || value->IsStringObject())
return true;
return false;
}
static v8::Handle<v8::Value> handlePostMessageCallback(const v8::Arguments& args, bool extendedTransfer)
{
DOMWindow* window = V8DOMWindow::toNative(args.Holder());
DOMWindow* source = V8Proxy::retrieveWindowForCallingContext();
MessagePortArray portArray;
ArrayBufferArray arrayBufferArray;
String targetOrigin;
{
v8::TryCatch tryCatch;
int targetOriginArgIndex = 1;
if (args.Length() > 2) {
int transferablesArgIndex = 2;
if (isLegacyTargetOriginDesignation(args[2])) {
targetOriginArgIndex = 2;
transferablesArgIndex = 1;
}
if (!extractTransferables(args[transferablesArgIndex], portArray, arrayBufferArray))
return v8::Undefined();
}
targetOrigin = toWebCoreStringWithNullOrUndefinedCheck(args[targetOriginArgIndex]);
if (tryCatch.HasCaught())
return v8::Undefined();
}
bool didThrow = false;
RefPtr<SerializedScriptValue> message =
SerializedScriptValue::create(args[0],
&portArray,
extendedTransfer ? &arrayBufferArray : 0,
didThrow,
args.GetIsolate());
if (didThrow)
return v8::Undefined();
ExceptionCode ec = 0;
window->postMessage(message.release(), &portArray, targetOrigin, source, ec);
return throwError(ec);
}
v8::Handle<v8::Value> V8DOMWindow::postMessageCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.postMessage()");
return handlePostMessageCallback(args, false);
}
v8::Handle<v8::Value> V8DOMWindow::webkitPostMessageCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.webkitPostMessage()");
return handlePostMessageCallback(args, true);
}
v8::Handle<v8::Value> V8DOMWindow::toStringCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.toString()");
v8::Handle<v8::Object> domWrapper = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), args.This());
if (domWrapper.IsEmpty())
return args.This()->ObjectProtoToString();
return domWrapper->ObjectProtoToString();
}
v8::Handle<v8::Value> V8DOMWindow::releaseEventsCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.nop()");
return v8::Undefined();
}
v8::Handle<v8::Value> V8DOMWindow::captureEventsCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.nop()");
return v8::Undefined();
}
class DialogHandler {
public:
explicit DialogHandler(v8::Handle<v8::Value> dialogArguments)
: m_dialogArguments(dialogArguments)
{
}
void dialogCreated(DOMWindow*);
v8::Handle<v8::Value> returnValue() const;
private:
v8::Handle<v8::Value> m_dialogArguments;
v8::Handle<v8::Context> m_dialogContext;
};
inline void DialogHandler::dialogCreated(DOMWindow* dialogFrame)
{
m_dialogContext = V8Proxy::context(dialogFrame->frame());
if (m_dialogContext.IsEmpty())
return;
if (m_dialogArguments.IsEmpty())
return;
v8::Context::Scope scope(m_dialogContext);
m_dialogContext->Global()->Set(v8::String::New("dialogArguments"), m_dialogArguments);
}
inline v8::Handle<v8::Value> DialogHandler::returnValue() const
{
if (m_dialogContext.IsEmpty())
return v8::Undefined();
v8::Context::Scope scope(m_dialogContext);
v8::Handle<v8::Value> returnValue = m_dialogContext->Global()->Get(v8::String::New("returnValue"));
if (returnValue.IsEmpty())
return v8::Undefined();
return returnValue;
}
static void setUpDialog(DOMWindow* dialog, void* handler)
{
static_cast<DialogHandler*>(handler)->dialogCreated(dialog);
}
v8::Handle<v8::Value> V8DOMWindow::showModalDialogCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.showModalDialog()");
DOMWindow* impl = V8DOMWindow::toNative(args.Holder());
V8BindingState* state = V8BindingState::Only();
if (!V8BindingSecurity::canAccessFrame(state, impl->frame(), true))
return v8::Undefined();
String urlString = toWebCoreStringWithNullOrUndefinedCheck(args[0]);
DialogHandler handler(args[1]);
String dialogFeaturesString = toWebCoreStringWithNullOrUndefinedCheck(args[2]);
DOMWindow* activeWindow = state->activeWindow();
DOMWindow* firstWindow = state->firstWindow();
impl->showModalDialog(urlString, dialogFeaturesString, activeWindow, firstWindow, setUpDialog, &handler);
return handler.returnValue();
}
v8::Handle<v8::Value> V8DOMWindow::openCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.open()");
DOMWindow* impl = V8DOMWindow::toNative(args.Holder());
V8BindingState* state = V8BindingState::Only();
if (!V8BindingSecurity::canAccessFrame(state, impl->frame(), true))
return v8::Undefined();
String urlString = toWebCoreStringWithNullOrUndefinedCheck(args[0]);
AtomicString frameName = (args[1]->IsUndefined() || args[1]->IsNull()) ? "_blank" : AtomicString(toWebCoreString(args[1]));
String windowFeaturesString = toWebCoreStringWithNullOrUndefinedCheck(args[2]);
DOMWindow* activeWindow = state->activeWindow();
DOMWindow* firstWindow = state->firstWindow();
RefPtr<DOMWindow> openedWindow = impl->open(urlString, frameName, windowFeaturesString, activeWindow, firstWindow);
if (!openedWindow)
return v8::Undefined();
return toV8(openedWindow.release(), args.GetIsolate());
}
v8::Handle<v8::Value> V8DOMWindow::indexedPropertyGetter(uint32_t index, const v8::AccessorInfo& info)
{
INC_STATS("DOM.DOMWindow.IndexedPropertyGetter");
DOMWindow* window = V8DOMWindow::toNative(info.Holder());
if (!window)
return notHandledByInterceptor();
Frame* frame = window->frame();
if (!frame)
return notHandledByInterceptor();
Frame* child = frame->tree()->scopedChild(index);
if (child)
return toV8(child->domWindow(), info.GetIsolate());
return notHandledByInterceptor();
}
v8::Handle<v8::Value> V8DOMWindow::namedPropertyGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
{
INC_STATS("DOM.DOMWindow.NamedPropertyGetter");
DOMWindow* window = V8DOMWindow::toNative(info.Holder());
if (!window)
return notHandledByInterceptor();
Frame* frame = window->frame();
if (!frame)
return notHandledByInterceptor();
AtomicString propName = v8StringToAtomicWebCoreString(name);
Frame* child = frame->tree()->scopedChild(propName);
if (child)
return toV8(child->domWindow(), info.GetIsolate());
if (!info.Holder()->GetRealNamedProperty(name).IsEmpty())
return notHandledByInterceptor();
Document* doc = frame->document();
if (doc && doc->isHTMLDocument()) {
if (static_cast<HTMLDocument*>(doc)->hasNamedItem(propName.impl()) || doc->hasElementWithId(propName.impl())) {
HTMLCollection* items = doc->windowNamedItems(propName);
if (items->length() >= 1) {
if (items->length() == 1)
return toV8(items->firstItem(), info.GetIsolate());
return toV8(items, info.GetIsolate());
}
}
}
return notHandledByInterceptor();
}
v8::Handle<v8::Value> V8DOMWindow::setTimeoutCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.setTimeout()");
return WindowSetTimeoutImpl(args, true);
}
v8::Handle<v8::Value> V8DOMWindow::setIntervalCallback(const v8::Arguments& args)
{
INC_STATS("DOM.DOMWindow.setInterval()");
return WindowSetTimeoutImpl(args, false);
}
bool V8DOMWindow::namedSecurityCheck(v8::Local<v8::Object> host, v8::Local<v8::Value> key, v8::AccessType type, v8::Local<v8::Value>)
{
v8::Handle<v8::Object> window = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), host);
if (window.IsEmpty())
return false;
DOMWindow* targetWindow = V8DOMWindow::toNative(window);
ASSERT(targetWindow);
Frame* target = targetWindow->frame();
if (!target)
return false;
if (key->IsString()) {
DEFINE_STATIC_LOCAL(AtomicString, nameOfProtoProperty, ("__proto__"));
String name = toWebCoreString(key);
Frame* childFrame = target->tree()->scopedChild(name);
if (type == v8::ACCESS_HAS && childFrame)
return true;
if (type == v8::ACCESS_GET && childFrame && !host->HasRealNamedProperty(key->ToString()) && name != nameOfProtoProperty)
return true;
}
return V8BindingSecurity::canAccessFrame(V8BindingState::Only(), target, false);
}
bool V8DOMWindow::indexedSecurityCheck(v8::Local<v8::Object> host, uint32_t index, v8::AccessType type, v8::Local<v8::Value>)
{
v8::Handle<v8::Object> window = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), host);
if (window.IsEmpty())
return false;
DOMWindow* targetWindow = V8DOMWindow::toNative(window);
ASSERT(targetWindow);
Frame* target = targetWindow->frame();
if (!target)
return false;
Frame* childFrame = target->tree()->scopedChild(index);
if (type == v8::ACCESS_HAS && childFrame)
return true;
if (type == v8::ACCESS_GET && childFrame && !host->HasRealIndexedProperty(index))
return true;
return V8BindingSecurity::canAccessFrame(V8BindingState::Only(), target, false);
}
v8::Handle<v8::Value> toV8(DOMWindow* window, v8::Isolate* isolate)
{
if (!window)
return v8::Null();
Frame* frame = window->frame();
if (!frame)
return v8::Handle<v8::Object>();
v8::Handle<v8::Context> currentContext = v8::Context::GetCurrent();
v8::Handle<v8::Object> currentGlobal = currentContext->Global();
v8::Handle<v8::Object> windowWrapper = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), currentGlobal);
if (!windowWrapper.IsEmpty()) {
if (V8DOMWindow::toNative(windowWrapper) == window)
return currentGlobal;
}
v8::Handle<v8::Context> context = V8Proxy::context(frame);
if (context.IsEmpty())
return v8::Handle<v8::Object>();
v8::Handle<v8::Object> global = context->Global();
ASSERT(!global.IsEmpty());
return global;
}
}