2011-02-13 Anders Carlsson <andersca@apple.com>
[WebKit-https.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 "WebProcessManager.h"
39 #include "WebProcessMessages.h"
40 #include "WebProcessProxyMessages.h"
41 #include "WebProcessProxyMessageKinds.h"
42 #include <WebCore/KURL.h>
43 #include <wtf/text/CString.h>
44 #include <wtf/text/WTFString.h>
45
46 using namespace WebCore;
47 using namespace std;
48
49 namespace WebKit {
50
51 template<typename HashMap>
52 static inline bool isGoodKey(const typename HashMap::KeyType& key)
53 {
54     return key != HashTraits<typename HashMap::KeyType>::emptyValue() && !HashTraits<typename HashMap::KeyType>::isDeletedValue(key);
55 }
56
57 static uint64_t generatePageID()
58 {
59     static uint64_t uniquePageID = 1;
60     return uniquePageID++;
61 }
62
63 PassRefPtr<WebProcessProxy> WebProcessProxy::create(WebContext* context)
64 {
65     return adoptRef(new WebProcessProxy(context));
66 }
67
68 WebProcessProxy::WebProcessProxy(WebContext* context)
69     : m_responsivenessTimer(this)
70     , m_context(context)
71 {
72     connect();
73 }
74
75 WebProcessProxy::~WebProcessProxy()
76 {
77     if (m_connection)
78         m_connection->invalidate();
79     
80     for (size_t i = 0; i < m_pendingMessages.size(); ++i)
81         m_pendingMessages[i].first.releaseArguments();
82
83     if (m_processLauncher) {
84         m_processLauncher->invalidate();
85         m_processLauncher = 0;
86     }
87
88     if (m_threadLauncher) {
89         m_threadLauncher->invalidate();
90         m_threadLauncher = 0;
91     }
92 }
93
94 void WebProcessProxy::connect()
95 {
96     if (m_context->processModel() == ProcessModelSharedSecondaryThread) {
97         ASSERT(!m_threadLauncher);
98         m_threadLauncher = ThreadLauncher::create(this);
99     } else {
100         ASSERT(!m_processLauncher);
101
102         ProcessLauncher::LaunchOptions launchOptions;
103         launchOptions.processType = ProcessLauncher::WebProcess;
104
105 #if PLATFORM(MAC)
106         // We want the web process to match the architecture of the UI process.
107         launchOptions.architecture = ProcessLauncher::LaunchOptions::MatchCurrentArchitecture;
108         launchOptions.executableHeap = false;
109 #endif
110         m_processLauncher = ProcessLauncher::create(this, launchOptions);
111     }
112 }
113
114 bool WebProcessProxy::sendMessage(CoreIPC::MessageID messageID, PassOwnPtr<CoreIPC::ArgumentEncoder> arguments, unsigned messageSendFlags)
115 {
116     // 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
117     // a CoreIPC connection.
118     if (isLaunching()) {
119         m_pendingMessages.append(make_pair(CoreIPC::Connection::OutgoingMessage(messageID, arguments), messageSendFlags));
120         return true;
121     }
122
123     // If the web process has exited, m_connection will be null here.
124     if (!m_connection)
125         return false;
126
127     return m_connection->sendMessage(messageID, arguments, messageSendFlags);
128 }
129
130 bool WebProcessProxy::isLaunching() const
131 {
132     if (m_processLauncher)
133         return m_processLauncher->isLaunching();
134     if (m_threadLauncher)
135         return m_threadLauncher->isLaunching();
136
137     return false;
138 }
139
140 void WebProcessProxy::terminate()
141 {
142     if (m_processLauncher)
143         m_processLauncher->terminateProcess();
144 }
145
146 WebPageProxy* WebProcessProxy::webPage(uint64_t pageID) const
147 {
148     return m_pageMap.get(pageID).get();
149 }
150
151 WebPageProxy* WebProcessProxy::createWebPage(PageClient* pageClient, WebContext* context, WebPageGroup* pageGroup)
152 {
153     ASSERT(context->process() == this);
154
155     unsigned pageID = generatePageID();
156     RefPtr<WebPageProxy> webPage = WebPageProxy::create(pageClient, context, pageGroup, pageID);
157     m_pageMap.set(pageID, webPage);
158     return webPage.get();
159 }
160
161 void WebProcessProxy::addExistingWebPage(WebPageProxy* webPage, uint64_t pageID)
162 {
163     m_pageMap.set(pageID, webPage);
164 }
165
166 void WebProcessProxy::removeWebPage(uint64_t pageID)
167 {
168     m_pageMap.remove(pageID);
169 }
170
171 WebProcessProxy::pages_const_iterator WebProcessProxy::pages_begin()
172 {
173     return m_pageMap.begin().values();
174 }
175
176 WebProcessProxy::pages_const_iterator WebProcessProxy::pages_end()
177 {
178     return m_pageMap.end().values();
179 }
180
181 size_t WebProcessProxy::numberOfPages()
182 {
183     return m_pageMap.size();
184 }
185
186 WebBackForwardListItem* WebProcessProxy::webBackForwardItem(uint64_t itemID) const
187 {
188     return m_backForwardListItemMap.get(itemID).get();
189 }
190
191 void WebProcessProxy::registerNewWebBackForwardListItem(WebBackForwardListItem* item)
192 {
193     // This item was just created by the UIProcess and is being added to the map for the first time
194     // so we should not already have an item for this ID.
195     ASSERT(!m_backForwardListItemMap.contains(item->itemID()));
196
197     m_backForwardListItemMap.set(item->itemID(), item);
198 }
199
200 void WebProcessProxy::addBackForwardItem(uint64_t itemID, const String& originalURL, const String& url, const String& title, const CoreIPC::DataReference& backForwardData)
201 {
202     std::pair<WebBackForwardListItemMap::iterator, bool> result = m_backForwardListItemMap.add(itemID, 0);
203     if (result.second) {
204         // New item.
205         result.first->second = WebBackForwardListItem::create(originalURL, url, title, backForwardData.data(), backForwardData.size(), itemID);
206         return;
207     }
208
209     // Update existing item.
210     result.first->second->setOriginalURL(originalURL);
211     result.first->second->setURL(url);
212     result.first->second->setTitle(title);
213     result.first->second->setBackForwardData(backForwardData.data(), backForwardData.size());
214 }
215
216 #if ENABLE(PLUGIN_PROCESS)
217 void WebProcessProxy::getPluginProcessConnection(const String& pluginPath, CoreIPC::ArgumentEncoder* reply)
218 {
219     PluginProcessManager::shared().getPluginProcessConnection(pluginPath, this, reply);
220 }
221 #endif
222
223 void WebProcessProxy::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments)
224 {
225     if (messageID.is<CoreIPC::MessageClassWebProcessProxy>()) {
226         didReceiveWebProcessProxyMessage(connection, messageID, arguments);
227         return;
228     }
229
230     if (messageID.is<CoreIPC::MessageClassWebContext>()
231         || messageID.is<CoreIPC::MessageClassWebContextLegacy>()
232         || messageID.is<CoreIPC::MessageClassDownloadProxy>()
233         || messageID.is<CoreIPC::MessageClassWebDatabaseManagerProxy>()
234         || messageID.is<CoreIPC::MessageClassWebGeolocationManagerProxy>()) {
235         m_context->didReceiveMessage(connection, messageID, arguments);
236         return;
237     }
238
239     uint64_t pageID = arguments->destinationID();
240     if (!pageID)
241         return;
242
243     WebPageProxy* pageProxy = webPage(pageID);
244     if (!pageProxy)
245         return;
246     
247     pageProxy->didReceiveMessage(connection, messageID, arguments);
248 }
249
250 CoreIPC::SyncReplyMode WebProcessProxy::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, CoreIPC::ArgumentEncoder* reply)
251 {
252 #if ENABLE(PLUGIN_PROCESS)
253     if (messageID.is<CoreIPC::MessageClassWebProcessProxyLegacy>()) {
254         switch (messageID.get<WebProcessProxyLegacyMessage::Kind>()) {
255             case WebProcessProxyLegacyMessage::GetPluginProcessConnection: {
256                 String pluginPath;
257                 
258                 if (!arguments->decode(CoreIPC::Out(pluginPath)))
259                     return CoreIPC::AutomaticReply;
260
261                 getPluginProcessConnection(pluginPath, reply);
262                 return CoreIPC::ManualReply;
263             }
264         }
265     }
266 #endif
267
268     if (messageID.is<CoreIPC::MessageClassWebContext>() || messageID.is<CoreIPC::MessageClassWebContextLegacy>()  || messageID.is<CoreIPC::MessageClassDownloadProxy>())
269         return m_context->didReceiveSyncMessage(connection, messageID, arguments, reply);
270
271     uint64_t pageID = arguments->destinationID();
272     if (!pageID)
273         return CoreIPC::AutomaticReply;
274     
275     WebPageProxy* pageProxy = webPage(pageID);
276     if (!pageProxy)
277         return CoreIPC::AutomaticReply;
278     
279     pageProxy->didReceiveSyncMessage(connection, messageID, arguments, reply);
280     return CoreIPC::AutomaticReply;
281 }
282
283 void WebProcessProxy::didClose(CoreIPC::Connection*)
284 {
285     // Protect ourselves, as the call to the shared WebProcessManager's processDidClose()
286     // below may otherwise cause us to be deleted before we can finish our work.
287     RefPtr<WebProcessProxy> protect(this);
288     
289     m_connection = nullptr;
290     m_responsivenessTimer.stop();
291
292     Vector<RefPtr<WebFrameProxy> > frames;
293     copyValuesToVector(m_frameMap, frames);
294
295     for (size_t i = 0, size = frames.size(); i < size; ++i)
296         frames[i]->disconnect();
297     m_frameMap.clear();
298
299     Vector<RefPtr<WebPageProxy> > pages;
300     copyValuesToVector(m_pageMap, pages);
301
302     m_context->processDidClose(this);
303
304     WebProcessManager::shared().processDidClose(this, m_context);
305
306     for (size_t i = 0, size = pages.size(); i < size; ++i)
307         pages[i]->processDidCrash();
308 }
309
310 void WebProcessProxy::didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::MessageID messageID)
311 {
312     // We received an invalid message from the web process, invalidate our connection and kill it.
313     m_connection->invalidate();
314
315     terminate();
316 }
317
318 void WebProcessProxy::didBecomeUnresponsive(ResponsivenessTimer*)
319 {
320     Vector<RefPtr<WebPageProxy> > pages;
321     copyValuesToVector(m_pageMap, pages);
322     for (size_t i = 0, size = pages.size(); i < size; ++i)
323         pages[i]->processDidBecomeUnresponsive();
324 }
325
326 void WebProcessProxy::didBecomeResponsive(ResponsivenessTimer*)
327 {
328     Vector<RefPtr<WebPageProxy> > pages;
329     copyValuesToVector(m_pageMap, pages);
330     for (size_t i = 0, size = pages.size(); i < size; ++i)
331         pages[i]->processDidBecomeResponsive();
332 }
333
334 void WebProcessProxy::didFinishLaunching(ProcessLauncher*, CoreIPC::Connection::Identifier connectionIdentifier)
335 {
336     didFinishLaunching(connectionIdentifier);
337 }
338
339 void WebProcessProxy::didFinishLaunching(ThreadLauncher*, CoreIPC::Connection::Identifier connectionIdentifier)
340 {
341     didFinishLaunching(connectionIdentifier);
342 }
343
344 void WebProcessProxy::didFinishLaunching(CoreIPC::Connection::Identifier connectionIdentifier)
345 {
346     ASSERT(!m_connection);
347     
348     m_connection = CoreIPC::Connection::createServerConnection(connectionIdentifier, this, RunLoop::main());
349 #if PLATFORM(MAC)
350     m_connection->setShouldCloseConnectionOnMachExceptions();
351 #elif PLATFORM(QT)
352     m_connection->setShouldCloseConnectionOnProcessTermination(processIdentifier());
353 #endif
354
355     m_connection->open();
356     
357     for (size_t i = 0; i < m_pendingMessages.size(); ++i) {
358         CoreIPC::Connection::OutgoingMessage& outgoingMessage = m_pendingMessages[i].first;
359         unsigned messageSendFlags = m_pendingMessages[i].second;
360         m_connection->sendMessage(outgoingMessage.messageID(), adoptPtr(outgoingMessage.arguments()), messageSendFlags);
361     }
362
363     m_pendingMessages.clear();
364
365     // Tell the context that we finished launching.
366     m_context->processDidFinishLaunching(this);
367 }
368
369 WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const
370 {
371     return isGoodKey<WebFrameProxyMap>(frameID) ? m_frameMap.get(frameID).get() : 0;
372 }
373
374 bool WebProcessProxy::canCreateFrame(uint64_t frameID) const
375 {
376     return isGoodKey<WebFrameProxyMap>(frameID) && !m_frameMap.contains(frameID);
377 }
378
379 void WebProcessProxy::frameCreated(uint64_t frameID, WebFrameProxy* frameProxy)
380 {
381     ASSERT(canCreateFrame(frameID));
382     m_frameMap.set(frameID, frameProxy);
383 }
384
385 void WebProcessProxy::didDestroyFrame(uint64_t frameID)
386 {
387     // If the page is closed before it has had the chance to send the DidCreateMainFrame message
388     // back to the UIProcess, then the frameDestroyed message will still be received because it
389     // gets sent directly to the WebProcessProxy.
390     ASSERT(isGoodKey<WebFrameProxyMap>(frameID));
391     m_frameMap.remove(frameID);
392 }
393
394 void WebProcessProxy::disconnectFramesFromPage(WebPageProxy* page)
395 {
396     Vector<RefPtr<WebFrameProxy> > frames;
397     copyValuesToVector(m_frameMap, frames);
398     for (size_t i = 0, size = frames.size(); i < size; ++i) {
399         if (frames[i]->page() == page)
400             frames[i]->disconnect();
401     }
402 }
403
404 size_t WebProcessProxy::frameCountInPage(WebPageProxy* page) const
405 {
406     size_t result = 0;
407     for (HashMap<uint64_t, RefPtr<WebFrameProxy> >::const_iterator iter = m_frameMap.begin(); iter != m_frameMap.end(); ++iter) {
408         if (iter->second->page() == page)
409             ++result;
410     }
411     return result;
412 }
413
414 void WebProcessProxy::updateTextCheckerState()
415 {
416     if (!isValid())
417         return;
418
419     send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0);
420 }
421
422 } // namespace WebKit