2011-04-20 Maciej Stachowiak <mjs@apple.com>
[WebKit.git] / Source / WebKit2 / UIProcess / WebProcessProxy.cpp
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>
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
55 static uint64_t generatePageID()
56 {
57     static uint64_t uniquePageID = 1;
58     return uniquePageID++;
59 }
60
61 PassRefPtr<WebProcessProxy> WebProcessProxy::create(PassRefPtr<WebContext> context)
62 {
63     return adoptRef(new WebProcessProxy(context));
64 }
65
66 WebProcessProxy::WebProcessProxy(PassRefPtr<WebContext> context)
67     : m_responsivenessTimer(this)
68     , m_context(context)
69 {
70     connect();
71 }
72
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
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
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
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
147 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
157 void WebProcessProxy::terminate()
158 {
159     if (m_processLauncher)
160         m_processLauncher->terminateProcess();
161 }
162
163 WebPageProxy* WebProcessProxy::webPage(uint64_t pageID) const
164 {
165     return m_pageMap.get(pageID);
166 }
167
168 PassRefPtr<WebPageProxy> WebProcessProxy::createWebPage(PageClient* pageClient, WebContext* context, WebPageGroup* pageGroup)
169 {
170     ASSERT(context->process() == this);
171
172     uint64_t 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
178 void WebProcessProxy::addExistingWebPage(WebPageProxy* webPage, uint64_t pageID)
179 {
180     m_pageMap.set(pageID, webPage);
181 }
182
183 void WebProcessProxy::removeWebPage(uint64_t pageID)
184 {
185     m_pageMap.remove(pageID);
186 }
187
188 WebBackForwardListItem* WebProcessProxy::webBackForwardItem(uint64_t itemID) const
189 {
190     return m_backForwardListItemMap.get(itemID).get();
191 }
192
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
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)
219 void WebProcessProxy::getPluginProcessConnection(const String& pluginPath, PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply)
220 {
221     PluginProcessManager::shared().getPluginProcessConnection(context()->pluginInfoStore(), pluginPath, reply);
222 }
223
224 void WebProcessProxy::pluginSyncMessageSendTimedOut(const String& pluginPath)
225 {
226     PluginProcessManager::shared().pluginSyncMessageSendTimedOut(pluginPath);
227 }
228 #endif
229
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
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
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
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
307 void WebProcessProxy::syncMessageSendTimedOut(CoreIPC::Connection*)
308 {
309 }
310
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
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
327 void WebProcessProxy::didFinishLaunching(ProcessLauncher*, CoreIPC::Connection::Identifier connectionIdentifier)
328 {
329     didFinishLaunching(connectionIdentifier);
330 }
331
332 void WebProcessProxy::didFinishLaunching(ThreadLauncher*, CoreIPC::Connection::Identifier connectionIdentifier)
333 {
334     didFinishLaunching(connectionIdentifier);
335 }
336
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
362 WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const
363 {
364     return isGoodKey<WebFrameProxyMap>(frameID) ? m_frameMap.get(frameID).get() : 0;
365 }
366
367 bool WebProcessProxy::canCreateFrame(uint64_t frameID) const
368 {
369     return isGoodKey<WebFrameProxyMap>(frameID) && !m_frameMap.contains(frameID);
370 }
371
372 void WebProcessProxy::frameCreated(uint64_t frameID, WebFrameProxy* frameProxy)
373 {
374     ASSERT(canCreateFrame(frameID));
375     m_frameMap.set(frameID, frameProxy);
376 }
377
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
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
397 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
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
420 void WebProcessProxy::updateTextCheckerState()
421 {
422     if (!isValid())
423         return;
424
425     send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0);
426 }
427
428 } // namespace WebKit