1/* 2 * Copyright (C) 2000 Harri Porten (porten@kde.org) 3 * Copyright (C) 2006 Jon Shier (jshier@iastate.edu) 4 * Copyright (C) 2003-2009, 2014 Apple Inc. All rights reseved. 5 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 20 * USA 21 */ 22 23#include "config.h" 24#include "JSDOMWindowBase.h" 25 26#include "Chrome.h" 27#include "DOMWindow.h" 28#include "Frame.h" 29#include "InspectorController.h" 30#include "JSDOMGlobalObjectTask.h" 31#include "JSDOMWindowCustom.h" 32#include "JSNode.h" 33#include "Logging.h" 34#include "Page.h" 35#include "ScriptController.h" 36#include "SecurityOrigin.h" 37#include "Settings.h" 38#include "WebCoreJSClientData.h" 39#include <runtime/Microtask.h> 40#include <wtf/MainThread.h> 41 42#if PLATFORM(IOS) 43#include "ChromeClient.h" 44#include "WebSafeGCActivityCallbackIOS.h" 45#include "WebSafeIncrementalSweeperIOS.h" 46#endif 47 48using namespace JSC; 49 50namespace WebCore { 51 52static bool shouldAllowAccessFrom(const JSGlobalObject* thisObject, ExecState* exec) 53{ 54 return BindingSecurity::shouldAllowAccessToDOMWindow(exec, asJSDOMWindow(thisObject)->impl()); 55} 56 57const ClassInfo JSDOMWindowBase::s_info = { "Window", &JSDOMGlobalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSDOMWindowBase) }; 58 59const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { &shouldAllowAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptExperimentsEnabled, &queueTaskToEventLoop, &shouldInterruptScriptBeforeTimeout }; 60 61JSDOMWindowBase::JSDOMWindowBase(VM& vm, Structure* structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell) 62 : JSDOMGlobalObject(vm, structure, &shell->world(), &s_globalObjectMethodTable) 63 , m_windowCloseWatchpoints((window && window->frame()) ? IsWatched : IsInvalidated) 64 , m_impl(window) 65 , m_shell(shell) 66{ 67} 68 69void JSDOMWindowBase::finishCreation(VM& vm, JSDOMWindowShell* shell) 70{ 71 Base::finishCreation(vm, shell); 72 ASSERT(inherits(info())); 73 74 GlobalPropertyInfo staticGlobals[] = { 75 GlobalPropertyInfo(vm.propertyNames->document, jsNull(), DontDelete | ReadOnly), 76 GlobalPropertyInfo(vm.propertyNames->window, m_shell, DontDelete | ReadOnly) 77 }; 78 79 addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals)); 80} 81 82void JSDOMWindowBase::destroy(JSCell* cell) 83{ 84 static_cast<JSDOMWindowBase*>(cell)->JSDOMWindowBase::~JSDOMWindowBase(); 85} 86 87void JSDOMWindowBase::updateDocument() 88{ 89 ASSERT(m_impl->document()); 90 ExecState* exec = globalExec(); 91 symbolTablePutWithAttributes(this, exec->vm(), exec->vm().propertyNames->document, toJS(exec, this, m_impl->document()), DontDelete | ReadOnly); 92} 93 94ScriptExecutionContext* JSDOMWindowBase::scriptExecutionContext() const 95{ 96 return m_impl->document(); 97} 98 99void JSDOMWindowBase::printErrorMessage(const String& message) const 100{ 101 printErrorMessageForFrame(impl().frame(), message); 102} 103 104bool JSDOMWindowBase::supportsProfiling(const JSGlobalObject* object) 105{ 106#if !ENABLE(INSPECTOR) 107 UNUSED_PARAM(object); 108 return false; 109#else 110 const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); 111 Frame* frame = thisObject->impl().frame(); 112 if (!frame) 113 return false; 114 115 Page* page = frame->page(); 116 if (!page) 117 return false; 118 119 return page->inspectorController().profilerEnabled(); 120#endif // ENABLE(INSPECTOR) 121} 122 123bool JSDOMWindowBase::supportsRichSourceInfo(const JSGlobalObject* object) 124{ 125#if !ENABLE(INSPECTOR) 126 UNUSED_PARAM(object); 127 return false; 128#else 129 const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); 130 Frame* frame = thisObject->impl().frame(); 131 if (!frame) 132 return false; 133 134 Page* page = frame->page(); 135 if (!page) 136 return false; 137 138 bool enabled = page->inspectorController().enabled(); 139 ASSERT(enabled || !thisObject->debugger()); 140 ASSERT(enabled || !supportsProfiling(thisObject)); 141 return enabled; 142#endif 143} 144 145static inline bool shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(Page* page) 146{ 147 // See <rdar://problem/5479443>. We don't think that page can ever be NULL 148 // in this case, but if it is, we've gotten into a state where we may have 149 // hung the UI, with no way to ask the client whether to cancel execution. 150 // For now, our solution is just to cancel execution no matter what, 151 // ensuring that we never hang. We might want to consider other solutions 152 // if we discover problems with this one. 153 ASSERT(page); 154 return !page; 155} 156 157bool JSDOMWindowBase::shouldInterruptScript(const JSGlobalObject* object) 158{ 159 const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); 160 ASSERT(thisObject->impl().frame()); 161 Page* page = thisObject->impl().frame()->page(); 162 return shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page) || page->chrome().shouldInterruptJavaScript(); 163} 164 165bool JSDOMWindowBase::shouldInterruptScriptBeforeTimeout(const JSGlobalObject* object) 166{ 167 const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); 168 ASSERT(thisObject->impl().frame()); 169 Page* page = thisObject->impl().frame()->page(); 170 171 if (shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page)) 172 return true; 173 174#if PLATFORM(IOS) 175 if (page->chrome().client().isStopping()) 176 return true; 177#endif 178 179 return JSGlobalObject::shouldInterruptScriptBeforeTimeout(object); 180} 181 182bool JSDOMWindowBase::javaScriptExperimentsEnabled(const JSGlobalObject* object) 183{ 184 const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); 185 Frame* frame = thisObject->impl().frame(); 186 if (!frame) 187 return false; 188 return frame->settings().javaScriptExperimentsEnabled(); 189} 190 191void JSDOMWindowBase::queueTaskToEventLoop(const JSGlobalObject* object, PassRefPtr<Microtask> task) 192{ 193 const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); 194 thisObject->scriptExecutionContext()->postTask(JSGlobalObjectTask((JSDOMWindowBase*)thisObject, task)); 195} 196 197void JSDOMWindowBase::willRemoveFromWindowShell() 198{ 199 setCurrentEvent(0); 200} 201 202JSDOMWindowShell* JSDOMWindowBase::shell() const 203{ 204 return m_shell; 205} 206 207VM& JSDOMWindowBase::commonVM() 208{ 209 ASSERT(isMainThread()); 210 211 static VM* vm = nullptr; 212 if (!vm) { 213 ScriptController::initializeThreading(); 214 vm = VM::createLeaked(LargeHeap).leakRef(); 215#if !PLATFORM(IOS) 216 vm->setExclusiveThread(std::this_thread::get_id()); 217#else 218 vm->heap.setFullActivityCallback(WebSafeFullGCActivityCallback::create(&vm->heap)); 219#if ENABLE(GGC) 220 vm->heap.setEdenActivityCallback(WebSafeEdenGCActivityCallback::create(&vm->heap)); 221#else 222 vm->heap.setEdenActivityCallback(vm->heap.fullActivityCallback()); 223#endif 224 vm->heap.setIncrementalSweeper(WebSafeIncrementalSweeper::create(&vm->heap)); 225 vm->makeUsableFromMultipleThreads(); 226 vm->heap.machineThreads().addCurrentThread(); 227#endif 228 initNormalWorldClientData(vm); 229 } 230 231 return *vm; 232} 233 234// JSDOMGlobalObject* is ignored, accessing a window in any context will 235// use that DOMWindow's prototype chain. 236JSValue toJS(ExecState* exec, JSDOMGlobalObject*, DOMWindow* domWindow) 237{ 238 return toJS(exec, domWindow); 239} 240 241JSValue toJS(ExecState* exec, DOMWindow* domWindow) 242{ 243 if (!domWindow) 244 return jsNull(); 245 Frame* frame = domWindow->frame(); 246 if (!frame) 247 return jsNull(); 248 return frame->script().windowShell(currentWorld(exec)); 249} 250 251JSDOMWindow* toJSDOMWindow(Frame* frame, DOMWrapperWorld& world) 252{ 253 if (!frame) 254 return 0; 255 return frame->script().windowShell(world)->window(); 256} 257 258JSDOMWindow* toJSDOMWindow(JSValue value) 259{ 260 if (!value.isObject()) 261 return 0; 262 const ClassInfo* classInfo = asObject(value)->classInfo(); 263 if (classInfo == JSDOMWindow::info()) 264 return jsCast<JSDOMWindow*>(asObject(value)); 265 if (classInfo == JSDOMWindowShell::info()) 266 return jsCast<JSDOMWindowShell*>(asObject(value))->window(); 267 return 0; 268} 269 270void JSDOMWindowBase::fireFrameClearedWatchpointsForWindow(DOMWindow* window) 271{ 272 JSC::VM& vm = JSDOMWindowBase::commonVM(); 273 WebCoreJSClientData* clientData = static_cast<WebCoreJSClientData*>(vm.clientData); 274 Vector<Ref<DOMWrapperWorld>> wrapperWorlds; 275 clientData->getAllWorlds(wrapperWorlds); 276 for (unsigned i = 0; i < wrapperWorlds.size(); ++i) { 277 DOMObjectWrapperMap& wrappers = wrapperWorlds[i]->m_wrappers; 278 auto result = wrappers.find(window); 279 if (result == wrappers.end()) 280 continue; 281 JSC::JSObject* wrapper = result->value.get(); 282 if (!wrapper) 283 continue; 284 JSDOMWindowBase* jsWindow = JSC::jsCast<JSDOMWindowBase*>(wrapper); 285 jsWindow->m_windowCloseWatchpoints.fireAll(); 286 } 287} 288 289} // namespace WebCore 290