1 /*
2 * Copyright (C) 2010, 2011 Apple Inc. 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "WebProcessProxy.h"
28
29 #include "DataReference.h"
30 #include "PluginInfoStore.h"
31 #include "PluginProcessManager.h"
32 #include "TextChecker.h"
33 #include "TextCheckerState.h"
34 #include "WebBackForwardListItem.h"
35 #include "WebContext.h"
36 #include "WebNavigationDataStore.h"
37 #include "WebPageProxy.h"
38 #include "WebProcessMessages.h"
39 #include "WebProcessProxyMessages.h"
40 #include <WebCore/KURL.h>
41 #include <wtf/text/CString.h>
42 #include <wtf/text/WTFString.h>
43
44 using namespace WebCore;
45 using namespace std;
46
47 namespace WebKit {
48
49 template<typename HashMap>
isGoodKey(const typename HashMap::KeyType & key)50 static inline bool isGoodKey(const typename HashMap::KeyType& key)
51 {
52 return key != HashTraits<typename HashMap::KeyType>::emptyValue() && !HashTraits<typename HashMap::KeyType>::isDeletedValue(key);
53 }
54
generatePageID()55 static uint64_t generatePageID()
56 {
57 static uint64_t uniquePageID = 1;
58 return uniquePageID++;
59 }
60
create(PassRefPtr<WebContext> context)61 PassRefPtr<WebProcessProxy> WebProcessProxy::create(PassRefPtr<WebContext> context)
62 {
63 return adoptRef(new WebProcessProxy(context));
64 }
65
WebProcessProxy(PassRefPtr<WebContext> context)66 WebProcessProxy::WebProcessProxy(PassRefPtr<WebContext> context)
67 : m_responsivenessTimer(this)
68 , m_context(context)
69 {
70 connect();
71 }
72
~WebProcessProxy()73 WebProcessProxy::~WebProcessProxy()
74 {
75 if (m_connection)
76 m_connection->invalidate();
77
78 for (size_t i = 0; i < m_pendingMessages.size(); ++i)
79 m_pendingMessages[i].first.releaseArguments();
80
81 if (m_processLauncher) {
82 m_processLauncher->invalidate();
83 m_processLauncher = 0;
84 }
85
86 if (m_threadLauncher) {
87 m_threadLauncher->invalidate();
88 m_threadLauncher = 0;
89 }
90 }
91
connect()92 void WebProcessProxy::connect()
93 {
94 if (m_context->processModel() == ProcessModelSharedSecondaryThread) {
95 ASSERT(!m_threadLauncher);
96 m_threadLauncher = ThreadLauncher::create(this);
97 } else {
98 ASSERT(!m_processLauncher);
99
100 ProcessLauncher::LaunchOptions launchOptions;
101 launchOptions.processType = ProcessLauncher::WebProcess;
102
103 #if PLATFORM(MAC)
104 // We want the web process to match the architecture of the UI process.
105 launchOptions.architecture = ProcessLauncher::LaunchOptions::MatchCurrentArchitecture;
106 launchOptions.executableHeap = false;
107 #endif
108 m_processLauncher = ProcessLauncher::create(this, launchOptions);
109 }
110 }
111
disconnect()112 void WebProcessProxy::disconnect()
113 {
114 if (m_connection) {
115 m_connection->invalidate();
116 m_connection = nullptr;
117 }
118
119 m_responsivenessTimer.stop();
120
121 Vector<RefPtr<WebFrameProxy> > frames;
122 copyValuesToVector(m_frameMap, frames);
123
124 for (size_t i = 0, size = frames.size(); i < size; ++i)
125 frames[i]->disconnect();
126 m_frameMap.clear();
127
128 m_context->disconnectProcess(this);
129 }
130
sendMessage(CoreIPC::MessageID messageID,PassOwnPtr<CoreIPC::ArgumentEncoder> arguments,unsigned messageSendFlags)131 bool WebProcessProxy::sendMessage(CoreIPC::MessageID messageID, PassOwnPtr<CoreIPC::ArgumentEncoder> arguments, unsigned messageSendFlags)
132 {
133 // If we're waiting for the web process to launch, we need to stash away the messages so we can send them once we have
134 // a CoreIPC connection.
135 if (isLaunching()) {
136 m_pendingMessages.append(make_pair(CoreIPC::Connection::OutgoingMessage(messageID, arguments), messageSendFlags));
137 return true;
138 }
139
140 // If the web process has exited, m_connection will be null here.
141 if (!m_connection)
142 return false;
143
144 return m_connection->sendMessage(messageID, arguments, messageSendFlags);
145 }
146
isLaunching() const147 bool WebProcessProxy::isLaunching() const
148 {
149 if (m_processLauncher)
150 return m_processLauncher->isLaunching();
151 if (m_threadLauncher)
152 return m_threadLauncher->isLaunching();
153
154 return false;
155 }
156
terminate()157 void WebProcessProxy::terminate()
158 {
159 if (m_processLauncher)
160 m_processLauncher->terminateProcess();
161 }
162
webPage(uint64_t pageID) const163 WebPageProxy* WebProcessProxy::webPage(uint64_t pageID) const
164 {
165 return m_pageMap.get(pageID);
166 }
167
createWebPage(PageClient * pageClient,WebContext * context,WebPageGroup * pageGroup)168 PassRefPtr<WebPageProxy> WebProcessProxy::createWebPage(PageClient* pageClient, WebContext* context, WebPageGroup* pageGroup)
169 {
170 ASSERT(context->process() == this);
171
172 unsigned pageID = generatePageID();
173 RefPtr<WebPageProxy> webPage = WebPageProxy::create(pageClient, this, pageGroup, pageID);
174 m_pageMap.set(pageID, webPage.get());
175 return webPage.release();
176 }
177
addExistingWebPage(WebPageProxy * webPage,uint64_t pageID)178 void WebProcessProxy::addExistingWebPage(WebPageProxy* webPage, uint64_t pageID)
179 {
180 m_pageMap.set(pageID, webPage);
181 }
182
removeWebPage(uint64_t pageID)183 void WebProcessProxy::removeWebPage(uint64_t pageID)
184 {
185 m_pageMap.remove(pageID);
186 }
187
webBackForwardItem(uint64_t itemID) const188 WebBackForwardListItem* WebProcessProxy::webBackForwardItem(uint64_t itemID) const
189 {
190 return m_backForwardListItemMap.get(itemID).get();
191 }
192
registerNewWebBackForwardListItem(WebBackForwardListItem * item)193 void WebProcessProxy::registerNewWebBackForwardListItem(WebBackForwardListItem* item)
194 {
195 // This item was just created by the UIProcess and is being added to the map for the first time
196 // so we should not already have an item for this ID.
197 ASSERT(!m_backForwardListItemMap.contains(item->itemID()));
198
199 m_backForwardListItemMap.set(item->itemID(), item);
200 }
201
addBackForwardItem(uint64_t itemID,const String & originalURL,const String & url,const String & title,const CoreIPC::DataReference & backForwardData)202 void WebProcessProxy::addBackForwardItem(uint64_t itemID, const String& originalURL, const String& url, const String& title, const CoreIPC::DataReference& backForwardData)
203 {
204 std::pair<WebBackForwardListItemMap::iterator, bool> result = m_backForwardListItemMap.add(itemID, 0);
205 if (result.second) {
206 // New item.
207 result.first->second = WebBackForwardListItem::create(originalURL, url, title, backForwardData.data(), backForwardData.size(), itemID);
208 return;
209 }
210
211 // Update existing item.
212 result.first->second->setOriginalURL(originalURL);
213 result.first->second->setURL(url);
214 result.first->second->setTitle(title);
215 result.first->second->setBackForwardData(backForwardData.data(), backForwardData.size());
216 }
217
218 #if ENABLE(PLUGIN_PROCESS)
getPluginProcessConnection(const String & pluginPath,PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply)219 void WebProcessProxy::getPluginProcessConnection(const String& pluginPath, PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply)
220 {
221 PluginProcessManager::shared().getPluginProcessConnection(context()->pluginInfoStore(), pluginPath, reply);
222 }
223
pluginSyncMessageSendTimedOut(const String & pluginPath)224 void WebProcessProxy::pluginSyncMessageSendTimedOut(const String& pluginPath)
225 {
226 PluginProcessManager::shared().pluginSyncMessageSendTimedOut(pluginPath);
227 }
228 #endif
229
didReceiveMessage(CoreIPC::Connection * connection,CoreIPC::MessageID messageID,CoreIPC::ArgumentDecoder * arguments)230 void WebProcessProxy::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments)
231 {
232 if (messageID.is<CoreIPC::MessageClassWebProcessProxy>()) {
233 didReceiveWebProcessProxyMessage(connection, messageID, arguments);
234 return;
235 }
236
237 if (messageID.is<CoreIPC::MessageClassWebContext>()
238 || messageID.is<CoreIPC::MessageClassWebContextLegacy>()
239 || messageID.is<CoreIPC::MessageClassDownloadProxy>()
240 || messageID.is<CoreIPC::MessageClassWebApplicationCacheManagerProxy>()
241 || messageID.is<CoreIPC::MessageClassWebCookieManagerProxy>()
242 || messageID.is<CoreIPC::MessageClassWebDatabaseManagerProxy>()
243 || messageID.is<CoreIPC::MessageClassWebGeolocationManagerProxy>()
244 || messageID.is<CoreIPC::MessageClassWebIconDatabase>()
245 || messageID.is<CoreIPC::MessageClassWebKeyValueStorageManagerProxy>()
246 || messageID.is<CoreIPC::MessageClassWebMediaCacheManagerProxy>()
247 || messageID.is<CoreIPC::MessageClassWebResourceCacheManagerProxy>()) {
248 m_context->didReceiveMessage(connection, messageID, arguments);
249 return;
250 }
251
252 uint64_t pageID = arguments->destinationID();
253 if (!pageID)
254 return;
255
256 WebPageProxy* pageProxy = webPage(pageID);
257 if (!pageProxy)
258 return;
259
260 pageProxy->didReceiveMessage(connection, messageID, arguments);
261 }
262
didReceiveSyncMessage(CoreIPC::Connection * connection,CoreIPC::MessageID messageID,CoreIPC::ArgumentDecoder * arguments,CoreIPC::ArgumentEncoder * reply)263 CoreIPC::SyncReplyMode WebProcessProxy::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, CoreIPC::ArgumentEncoder* reply)
264 {
265 if (messageID.is<CoreIPC::MessageClassWebProcessProxy>())
266 return didReceiveSyncWebProcessProxyMessage(connection, messageID, arguments, reply);
267
268 if (messageID.is<CoreIPC::MessageClassWebContext>() || messageID.is<CoreIPC::MessageClassWebContextLegacy>()
269 || messageID.is<CoreIPC::MessageClassDownloadProxy>() || messageID.is<CoreIPC::MessageClassWebIconDatabase>())
270 return m_context->didReceiveSyncMessage(connection, messageID, arguments, reply);
271
272 uint64_t pageID = arguments->destinationID();
273 if (!pageID)
274 return CoreIPC::AutomaticReply;
275
276 WebPageProxy* pageProxy = webPage(pageID);
277 if (!pageProxy)
278 return CoreIPC::AutomaticReply;
279
280 pageProxy->didReceiveSyncMessage(connection, messageID, arguments, reply);
281 return CoreIPC::AutomaticReply;
282 }
283
didClose(CoreIPC::Connection *)284 void WebProcessProxy::didClose(CoreIPC::Connection*)
285 {
286 // Protect ourselves, as the call to disconnect() below may otherwise cause us
287 // to be deleted before we can finish our work.
288 RefPtr<WebProcessProxy> protect(this);
289
290 Vector<RefPtr<WebPageProxy> > pages;
291 copyValuesToVector(m_pageMap, pages);
292
293 disconnect();
294
295 for (size_t i = 0, size = pages.size(); i < size; ++i)
296 pages[i]->processDidCrash();
297 }
298
didReceiveInvalidMessage(CoreIPC::Connection *,CoreIPC::MessageID messageID)299 void WebProcessProxy::didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::MessageID messageID)
300 {
301 // We received an invalid message from the web process, invalidate our connection and kill it.
302 m_connection->invalidate();
303
304 terminate();
305 }
306
syncMessageSendTimedOut(CoreIPC::Connection *)307 void WebProcessProxy::syncMessageSendTimedOut(CoreIPC::Connection*)
308 {
309 }
310
didBecomeUnresponsive(ResponsivenessTimer *)311 void WebProcessProxy::didBecomeUnresponsive(ResponsivenessTimer*)
312 {
313 Vector<RefPtr<WebPageProxy> > pages;
314 copyValuesToVector(m_pageMap, pages);
315 for (size_t i = 0, size = pages.size(); i < size; ++i)
316 pages[i]->processDidBecomeUnresponsive();
317 }
318
didBecomeResponsive(ResponsivenessTimer *)319 void WebProcessProxy::didBecomeResponsive(ResponsivenessTimer*)
320 {
321 Vector<RefPtr<WebPageProxy> > pages;
322 copyValuesToVector(m_pageMap, pages);
323 for (size_t i = 0, size = pages.size(); i < size; ++i)
324 pages[i]->processDidBecomeResponsive();
325 }
326
didFinishLaunching(ProcessLauncher *,CoreIPC::Connection::Identifier connectionIdentifier)327 void WebProcessProxy::didFinishLaunching(ProcessLauncher*, CoreIPC::Connection::Identifier connectionIdentifier)
328 {
329 didFinishLaunching(connectionIdentifier);
330 }
331
didFinishLaunching(ThreadLauncher *,CoreIPC::Connection::Identifier connectionIdentifier)332 void WebProcessProxy::didFinishLaunching(ThreadLauncher*, CoreIPC::Connection::Identifier connectionIdentifier)
333 {
334 didFinishLaunching(connectionIdentifier);
335 }
336
didFinishLaunching(CoreIPC::Connection::Identifier connectionIdentifier)337 void WebProcessProxy::didFinishLaunching(CoreIPC::Connection::Identifier connectionIdentifier)
338 {
339 ASSERT(!m_connection);
340
341 m_connection = CoreIPC::Connection::createServerConnection(connectionIdentifier, this, RunLoop::main());
342 #if PLATFORM(MAC)
343 m_connection->setShouldCloseConnectionOnMachExceptions();
344 #elif PLATFORM(QT) || PLATFORM(GTK)
345 m_connection->setShouldCloseConnectionOnProcessTermination(processIdentifier());
346 #endif
347
348 m_connection->open();
349
350 for (size_t i = 0; i < m_pendingMessages.size(); ++i) {
351 CoreIPC::Connection::OutgoingMessage& outgoingMessage = m_pendingMessages[i].first;
352 unsigned messageSendFlags = m_pendingMessages[i].second;
353 m_connection->sendMessage(outgoingMessage.messageID(), adoptPtr(outgoingMessage.arguments()), messageSendFlags);
354 }
355
356 m_pendingMessages.clear();
357
358 // Tell the context that we finished launching.
359 m_context->processDidFinishLaunching(this);
360 }
361
webFrame(uint64_t frameID) const362 WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const
363 {
364 return isGoodKey<WebFrameProxyMap>(frameID) ? m_frameMap.get(frameID).get() : 0;
365 }
366
canCreateFrame(uint64_t frameID) const367 bool WebProcessProxy::canCreateFrame(uint64_t frameID) const
368 {
369 return isGoodKey<WebFrameProxyMap>(frameID) && !m_frameMap.contains(frameID);
370 }
371
frameCreated(uint64_t frameID,WebFrameProxy * frameProxy)372 void WebProcessProxy::frameCreated(uint64_t frameID, WebFrameProxy* frameProxy)
373 {
374 ASSERT(canCreateFrame(frameID));
375 m_frameMap.set(frameID, frameProxy);
376 }
377
didDestroyFrame(uint64_t frameID)378 void WebProcessProxy::didDestroyFrame(uint64_t frameID)
379 {
380 // If the page is closed before it has had the chance to send the DidCreateMainFrame message
381 // back to the UIProcess, then the frameDestroyed message will still be received because it
382 // gets sent directly to the WebProcessProxy.
383 ASSERT(isGoodKey<WebFrameProxyMap>(frameID));
384 m_frameMap.remove(frameID);
385 }
386
disconnectFramesFromPage(WebPageProxy * page)387 void WebProcessProxy::disconnectFramesFromPage(WebPageProxy* page)
388 {
389 Vector<RefPtr<WebFrameProxy> > frames;
390 copyValuesToVector(m_frameMap, frames);
391 for (size_t i = 0, size = frames.size(); i < size; ++i) {
392 if (frames[i]->page() == page)
393 frames[i]->disconnect();
394 }
395 }
396
frameCountInPage(WebPageProxy * page) const397 size_t WebProcessProxy::frameCountInPage(WebPageProxy* page) const
398 {
399 size_t result = 0;
400 for (HashMap<uint64_t, RefPtr<WebFrameProxy> >::const_iterator iter = m_frameMap.begin(); iter != m_frameMap.end(); ++iter) {
401 if (iter->second->page() == page)
402 ++result;
403 }
404 return result;
405 }
406
shouldTerminate(bool & shouldTerminate)407 void WebProcessProxy::shouldTerminate(bool& shouldTerminate)
408 {
409 if (!m_pageMap.isEmpty() || !m_context->shouldTerminate(this)) {
410 shouldTerminate = false;
411 return;
412 }
413
414 shouldTerminate = true;
415
416 // We know that the web process is going to terminate so disconnect it from the context.
417 disconnect();
418 }
419
updateTextCheckerState()420 void WebProcessProxy::updateTextCheckerState()
421 {
422 if (!isValid())
423 return;
424
425 send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0);
426 }
427
428 } // namespace WebKit
429