1/*
2 * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
3 * Copyright (c) 2011 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "PageScriptDebugServer.h"
29
30#include "CommonVM.h"
31#include "Document.h"
32#include "EventLoop.h"
33#include "Frame.h"
34#include "FrameView.h"
35#include "InspectorController.h"
36#include "InspectorFrontendClient.h"
37#include "JSDOMExceptionHandling.h"
38#include "JSDOMWindowCustom.h"
39#include "Page.h"
40#include "PageGroup.h"
41#include "PluginViewBase.h"
42#include "ScriptController.h"
43#include "Timer.h"
44#include <JavaScriptCore/JSLock.h>
45#include <wtf/MainThread.h>
46#include <wtf/StdLibExtras.h>
47
48#if PLATFORM(IOS_FAMILY)
49#include "WebCoreThreadInternal.h"
50#endif
51
52
53namespace WebCore {
54using namespace JSC;
55using namespace Inspector;
56
57PageScriptDebugServer::PageScriptDebugServer(Page& page)
58 : ScriptDebugServer(WebCore::commonVM())
59 , m_page(page)
60{
61}
62
63void PageScriptDebugServer::attachDebugger()
64{
65 m_page.setDebugger(this);
66}
67
68void PageScriptDebugServer::detachDebugger(bool isBeingDestroyed)
69{
70 m_page.setDebugger(nullptr);
71 if (!isBeingDestroyed)
72 recompileAllJSFunctions();
73}
74
75void PageScriptDebugServer::recompileAllJSFunctions()
76{
77 JSLockHolder lock(vm());
78 Debugger::recompileAllJSFunctions();
79}
80
81void PageScriptDebugServer::didPause(JSGlobalObject*)
82{
83 setJavaScriptPaused(m_page.group(), true);
84}
85
86void PageScriptDebugServer::didContinue(JSGlobalObject*)
87{
88 setJavaScriptPaused(m_page.group(), false);
89}
90
91void PageScriptDebugServer::runEventLoopWhilePaused()
92{
93#if PLATFORM(IOS_FAMILY)
94 // On iOS, running an EventLoop causes us to run a nested WebRunLoop.
95 // Since the WebThread is autoreleased at the end of run loop iterations
96 // we need to gracefully handle releasing and reacquiring the lock.
97 if (WebThreadIsEnabled()) {
98 ASSERT(WebThreadIsLockedOrDisabled());
99 JSC::JSLock::DropAllLocks dropAllLocks(vm());
100 WebRunLoopEnableNested();
101
102 runEventLoopWhilePausedInternal();
103
104 WebRunLoopDisableNested();
105 ASSERT(WebThreadIsLockedOrDisabled());
106 return;
107 }
108#endif
109
110 runEventLoopWhilePausedInternal();
111}
112
113void PageScriptDebugServer::runEventLoopWhilePausedInternal()
114{
115 TimerBase::fireTimersInNestedEventLoop();
116
117 m_page.incrementNestedRunLoopCount();
118
119 EventLoop loop;
120 while (!m_doneProcessingDebuggerEvents && !loop.ended())
121 loop.cycle();
122
123 m_page.decrementNestedRunLoopCount();
124}
125
126bool PageScriptDebugServer::isContentScript(ExecState* state) const
127{
128 return &currentWorld(*state) != &mainThreadNormalWorld();
129}
130
131void PageScriptDebugServer::reportException(ExecState* state, JSC::Exception* exception) const
132{
133 WebCore::reportException(state, exception);
134}
135
136void PageScriptDebugServer::setJavaScriptPaused(const PageGroup& pageGroup, bool paused)
137{
138 setMainThreadCallbacksPaused(paused);
139
140 for (auto& page : pageGroup.pages()) {
141 for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext())
142 setJavaScriptPaused(*frame, paused);
143
144 if (auto* frontendClient = page->inspectorController().inspectorFrontendClient()) {
145 if (paused)
146 frontendClient->pagePaused();
147 else
148 frontendClient->pageUnpaused();
149 }
150 }
151}
152
153void PageScriptDebugServer::setJavaScriptPaused(Frame& frame, bool paused)
154{
155 if (!frame.script().canExecuteScripts(NotAboutToExecuteScript))
156 return;
157
158 frame.script().setPaused(paused);
159
160 ASSERT(frame.document());
161 auto& document = *frame.document();
162 if (paused) {
163 document.suspendScriptedAnimationControllerCallbacks();
164 document.suspendActiveDOMObjects(ReasonForSuspension::JavaScriptDebuggerPaused);
165 } else {
166 document.resumeActiveDOMObjects(ReasonForSuspension::JavaScriptDebuggerPaused);
167 document.resumeScriptedAnimationControllerCallbacks();
168 }
169
170 if (auto* view = frame.view()) {
171 for (auto& child : view->children()) {
172 if (!is<PluginViewBase>(child))
173 continue;
174 downcast<PluginViewBase>(child.get()).setJavaScriptPaused(paused);
175 }
176 }
177}
178
179} // namespace WebCore
180