1bb6bc4a2e5eafb4f219d333d39e624331013ac6
[WebKit.git] / Source / WebKit2 / UIProcess / WebContext.cpp
1 /*
2  * Copyright (C) 2010 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 "WebContext.h"
27
28 #include "DownloadProxy.h"
29 #include "ImmutableArray.h"
30 #include "InjectedBundleMessageKinds.h"
31 #include "RunLoop.h"
32 #include "SandboxExtension.h"
33 #include "TextChecker.h"
34 #include "WKContextPrivate.h"
35 #include "WebContextMessageKinds.h"
36 #include "WebContextUserMessageCoders.h"
37 #include "WebCoreArgumentCoders.h"
38 #include "WebDatabaseManagerProxy.h"
39 #include "WebGeolocationManagerProxy.h"
40 #include "WebPageGroup.h"
41 #include "WebMemorySampler.h"
42 #include "WebProcessCreationParameters.h"
43 #include "WebProcessManager.h"
44 #include "WebProcessMessages.h"
45 #include "WebProcessProxy.h"
46 #include <WebCore/Language.h>
47 #include <WebCore/LinkHash.h>
48 #include <wtf/CurrentTime.h>
49
50 #ifndef NDEBUG
51 #include <wtf/RefCountedLeakCounter.h>
52 #endif
53
54 using namespace WebCore;
55
56 namespace WebKit {
57
58 #ifndef NDEBUG
59 static WTF::RefCountedLeakCounter webContextCounter("WebContext");
60 #endif
61
62 WebContext* WebContext::sharedProcessContext()
63 {
64     WTF::initializeMainThread();
65     RunLoop::initializeMainRunLoop();
66     static WebContext* context = adoptRef(new WebContext(ProcessModelSharedSecondaryProcess, String())).leakRef();
67     return context;
68 }
69
70 WebContext* WebContext::sharedThreadContext()
71 {
72     RunLoop::initializeMainRunLoop();
73     static WebContext* context = adoptRef(new WebContext(ProcessModelSharedSecondaryThread, String())).leakRef();
74     return context;
75 }
76
77 PassRefPtr<WebContext> WebContext::create(const String& injectedBundlePath)
78 {
79     WTF::initializeMainThread();
80     RunLoop::initializeMainRunLoop();
81     return adoptRef(new WebContext(ProcessModelSecondaryProcess, injectedBundlePath));
82 }
83     
84 WebContext::WebContext(ProcessModel processModel, const String& injectedBundlePath)
85     : m_processModel(processModel)
86     , m_defaultPageGroup(WebPageGroup::create())
87     , m_injectedBundlePath(injectedBundlePath)
88     , m_visitedLinkProvider(this)
89     , m_alwaysUsesComplexTextCodePath(false)
90     , m_cacheModel(CacheModelDocumentViewer)
91     , m_clearResourceCachesForNewWebProcess(false)
92     , m_clearApplicationCacheForNewWebProcess(false)
93     , m_memorySamplerEnabled(false)
94     , m_memorySamplerInterval(1400.0)
95     , m_databaseManagerProxy(WebDatabaseManagerProxy::create(this))
96     , m_geolocationManagerProxy(WebGeolocationManagerProxy::create(this))
97 #if PLATFORM(WIN)
98     , m_shouldPaintNativeControls(true)
99 #endif
100 {
101     addLanguageChangeObserver(this, languageChanged);
102
103 #ifndef NDEBUG
104     webContextCounter.increment();
105 #endif
106 }
107
108 WebContext::~WebContext()
109 {
110     removeLanguageChangeObserver(this);
111
112     WebProcessManager::shared().contextWasDestroyed(this);
113
114     m_geolocationManagerProxy->invalidate();
115     m_geolocationManagerProxy->clearContext();
116
117     m_databaseManagerProxy->invalidate();
118     m_databaseManagerProxy->clearContext();
119
120 #ifndef NDEBUG
121     webContextCounter.decrement();
122 #endif
123 }
124
125 void WebContext::initializeInjectedBundleClient(const WKContextInjectedBundleClient* client)
126 {
127     m_injectedBundleClient.initialize(client);
128 }
129
130 void WebContext::initializeHistoryClient(const WKContextHistoryClient* client)
131 {
132     m_historyClient.initialize(client);
133     
134     if (!hasValidProcess())
135         return;
136         
137     m_process->send(Messages::WebProcess::SetShouldTrackVisitedLinks(m_historyClient.shouldTrackVisitedLinks()), 0);
138 }
139
140 void WebContext::initializeDownloadClient(const WKContextDownloadClient* client)
141 {
142     m_downloadClient.initialize(client);
143 }
144     
145 void WebContext::languageChanged(void* context)
146 {
147     static_cast<WebContext*>(context)->languageChanged();
148 }
149
150 void WebContext::languageChanged()
151 {
152     if (!hasValidProcess())
153         return;
154
155     m_process->send(Messages::WebProcess::LanguageChanged(defaultLanguage()), 0);
156 }
157
158 void WebContext::ensureWebProcess()
159 {
160     if (m_process)
161         return;
162
163     m_process = WebProcessManager::shared().getWebProcess(this);
164
165     WebProcessCreationParameters parameters;
166
167     parameters.applicationCacheDirectory = applicationCacheDirectory();
168
169     if (!injectedBundlePath().isEmpty()) {
170         parameters.injectedBundlePath = injectedBundlePath();
171
172         SandboxExtension::createHandle(parameters.injectedBundlePath, SandboxExtension::ReadOnly, parameters.injectedBundlePathExtensionHandle);
173     }
174
175     parameters.shouldTrackVisitedLinks = m_historyClient.shouldTrackVisitedLinks();
176     parameters.cacheModel = m_cacheModel;
177     parameters.languageCode = defaultLanguage();
178     parameters.applicationCacheDirectory = applicationCacheDirectory();
179     parameters.clearResourceCaches = m_clearResourceCachesForNewWebProcess;
180     parameters.clearApplicationCache = m_clearApplicationCacheForNewWebProcess;
181 #if PLATFORM(MAC)
182     parameters.presenterApplicationPid = getpid();
183 #endif
184
185     m_clearResourceCachesForNewWebProcess = false;
186     m_clearApplicationCacheForNewWebProcess = false;
187     
188     copyToVector(m_schemesToRegisterAsEmptyDocument, parameters.urlSchemesRegistererdAsEmptyDocument);
189     copyToVector(m_schemesToRegisterAsSecure, parameters.urlSchemesRegisteredAsSecure);
190     copyToVector(m_schemesToSetDomainRelaxationForbiddenFor, parameters.urlSchemesForWhichDomainRelaxationIsForbidden);
191
192     parameters.shouldAlwaysUseComplexTextCodePath = m_alwaysUsesComplexTextCodePath;
193
194     parameters.textCheckerState = TextChecker::state();
195
196     // Add any platform specific parameters
197     platformInitializeWebProcess(parameters);
198
199     m_process->send(Messages::WebProcess::InitializeWebProcess(parameters, WebContextUserMessageEncoder(m_injectedBundleInitializationUserData.get())), 0);
200
201     for (size_t i = 0; i != m_pendingMessagesToPostToInjectedBundle.size(); ++i) {
202         pair<String, RefPtr<APIObject> >& message = m_pendingMessagesToPostToInjectedBundle[i];
203         m_process->send(InjectedBundleMessage::PostMessage, 0, CoreIPC::In(message.first, WebContextUserMessageEncoder(message.second.get())));
204     }
205     m_pendingMessagesToPostToInjectedBundle.clear();
206 }
207
208 void WebContext::processDidFinishLaunching(WebProcessProxy* process)
209 {
210     // FIXME: Once we support multiple processes per context, this assertion won't hold.
211     ASSERT_UNUSED(process, process == m_process);
212
213     m_visitedLinkProvider.processDidFinishLaunching();
214     
215     // Sometimes the memorySampler gets initialized after process initialization has happened but before the process has finished launching
216     // so check if it needs to be started here
217     if(m_memorySamplerEnabled) {
218         SandboxExtension::Handle sampleLogSandboxHandle;        
219         double now = WTF::currentTime();
220         String sampleLogFilePath = String::format("WebProcess%llu", static_cast<uint64_t>(now));
221         sampleLogFilePath = SandboxExtension::createHandleForTemporaryFile(sampleLogFilePath, SandboxExtension::WriteOnly, sampleLogSandboxHandle);
222         
223         m_process->send(Messages::WebProcess::StartMemorySampler(sampleLogSandboxHandle, sampleLogFilePath, m_memorySamplerInterval), 0);
224     }
225 }
226
227 void WebContext::processDidClose(WebProcessProxy* process)
228 {
229     // FIXME: Once we support multiple processes per context, this assertion won't hold.
230     ASSERT_UNUSED(process, process == m_process);
231
232     m_visitedLinkProvider.processDidClose();
233
234     // Invalidate all outstanding downloads.
235     for (HashMap<uint64_t, RefPtr<DownloadProxy> >::iterator::Values it = m_downloads.begin().values(), end = m_downloads.end().values(); it != end; ++it) {
236         (*it)->processDidClose();
237         (*it)->invalidate();
238     }
239
240     m_downloads.clear();
241
242     m_databaseManagerProxy->invalidate();
243     m_geolocationManagerProxy->invalidate();
244
245     m_process = 0;
246 }
247
248 WebPageProxy* WebContext::createWebPage(PageClient* pageClient, WebPageGroup* pageGroup)
249 {
250     ensureWebProcess();
251
252     if (!pageGroup)
253         pageGroup = m_defaultPageGroup.get();
254
255     return m_process->createWebPage(pageClient, this, pageGroup);
256 }
257
258 void WebContext::relaunchProcessIfNecessary()
259 {
260     ensureWebProcess();
261 }
262
263 void WebContext::download(WebPageProxy* initiatingPage, const ResourceRequest& request)
264 {
265     uint64_t downloadID = createDownloadProxy();
266     uint64_t initiatingPageID = initiatingPage ? initiatingPage->pageID() : 0;
267
268     process()->send(Messages::WebProcess::DownloadRequest(downloadID, initiatingPageID, request), 0);
269 }
270
271 void WebContext::postMessageToInjectedBundle(const String& messageName, APIObject* messageBody)
272 {
273     if (!m_process || !m_process->canSendMessage()) {
274         m_pendingMessagesToPostToInjectedBundle.append(make_pair(messageName, messageBody));
275         return;
276     }
277
278     // FIXME: We should consider returning false from this function if the messageBody cannot
279     // be encoded.
280     m_process->send(InjectedBundleMessage::PostMessage, 0, CoreIPC::In(messageName, WebContextUserMessageEncoder(messageBody)));
281 }
282
283 // InjectedBundle client
284
285 void WebContext::didReceiveMessageFromInjectedBundle(const String& messageName, APIObject* messageBody)
286 {
287     m_injectedBundleClient.didReceiveMessageFromInjectedBundle(this, messageName, messageBody);
288 }
289
290 void WebContext::didReceiveSynchronousMessageFromInjectedBundle(const String& messageName, APIObject* messageBody, RefPtr<APIObject>& returnData)
291 {
292     m_injectedBundleClient.didReceiveSynchronousMessageFromInjectedBundle(this, messageName, messageBody, returnData);
293 }
294
295 // HistoryClient
296
297 void WebContext::didNavigateWithNavigationData(uint64_t pageID, const WebNavigationDataStore& store, uint64_t frameID) 
298 {
299     WebFrameProxy* frame = m_process->webFrame(frameID);
300     if (!frame->page())
301         return;
302     
303     m_historyClient.didNavigateWithNavigationData(this, frame->page(), store, frame);
304 }
305
306 void WebContext::didPerformClientRedirect(uint64_t pageID, const String& sourceURLString, const String& destinationURLString, uint64_t frameID)
307 {
308     WebFrameProxy* frame = m_process->webFrame(frameID);
309     if (!frame->page())
310         return;
311     
312     m_historyClient.didPerformClientRedirect(this, frame->page(), sourceURLString, destinationURLString, frame);
313 }
314
315 void WebContext::didPerformServerRedirect(uint64_t pageID, const String& sourceURLString, const String& destinationURLString, uint64_t frameID)
316 {
317     WebFrameProxy* frame = m_process->webFrame(frameID);
318     if (!frame->page())
319         return;
320     
321     m_historyClient.didPerformServerRedirect(this, frame->page(), sourceURLString, destinationURLString, frame);
322 }
323
324 void WebContext::didUpdateHistoryTitle(uint64_t pageID, const String& title, const String& url, uint64_t frameID)
325 {
326     WebFrameProxy* frame = m_process->webFrame(frameID);
327     if (!frame->page())
328         return;
329
330     m_historyClient.didUpdateHistoryTitle(this, frame->page(), title, url, frame);
331 }
332
333 void WebContext::populateVisitedLinks()
334 {
335     m_historyClient.populateVisitedLinks(this);
336 }
337
338 WebContext::Statistics& WebContext::statistics()
339 {
340     static Statistics statistics = Statistics();
341
342     return statistics;
343 }
344
345 void WebContext::setAdditionalPluginsDirectory(const String& directory)
346 {
347     Vector<String> directories;
348     directories.append(directory);
349
350     m_pluginInfoStore.setAdditionalPluginsDirectories(directories);
351 }
352
353 void WebContext::setAlwaysUsesComplexTextCodePath(bool alwaysUseComplexText)
354 {
355     m_alwaysUsesComplexTextCodePath = alwaysUseComplexText;
356
357     if (!hasValidProcess())
358         return;
359
360     m_process->send(Messages::WebProcess::SetAlwaysUsesComplexTextCodePath(alwaysUseComplexText), 0);
361 }
362
363 void WebContext::registerURLSchemeAsEmptyDocument(const String& urlScheme)
364 {
365     m_schemesToRegisterAsEmptyDocument.add(urlScheme);
366
367     if (!hasValidProcess())
368         return;
369
370     m_process->send(Messages::WebProcess::RegisterURLSchemeAsEmptyDocument(urlScheme), 0);
371 }
372
373 void WebContext::registerURLSchemeAsSecure(const String& urlScheme)
374 {
375     m_schemesToRegisterAsSecure.add(urlScheme);
376
377     if (!hasValidProcess())
378         return;
379
380     m_process->send(Messages::WebProcess::RegisterURLSchemeAsSecure(urlScheme), 0);
381 }
382
383 void WebContext::setDomainRelaxationForbiddenForURLScheme(const String& urlScheme)
384 {
385     m_schemesToSetDomainRelaxationForbiddenFor.add(urlScheme);
386
387     if (!hasValidProcess())
388         return;
389
390     m_process->send(Messages::WebProcess::SetDomainRelaxationForbiddenForURLScheme(urlScheme), 0);
391 }
392
393 void WebContext::setCacheModel(CacheModel cacheModel)
394 {
395     m_cacheModel = cacheModel;
396
397     if (!hasValidProcess())
398         return;
399     m_process->send(Messages::WebProcess::SetCacheModel(static_cast<uint32_t>(m_cacheModel)), 0);
400 }
401
402 void WebContext::addVisitedLink(const String& visitedURL)
403 {
404     if (visitedURL.isEmpty())
405         return;
406
407     LinkHash linkHash = visitedLinkHash(visitedURL.characters(), visitedURL.length());
408     addVisitedLinkHash(linkHash);
409 }
410
411 void WebContext::addVisitedLinkHash(LinkHash linkHash)
412 {
413     m_visitedLinkProvider.addVisitedLink(linkHash);
414 }
415
416 void WebContext::getPlugins(bool refresh, Vector<PluginInfo>& plugins)
417 {
418     if (refresh)
419         pluginInfoStore()->refresh();
420     pluginInfoStore()->getPlugins(plugins);
421 }
422
423 void WebContext::getPluginPath(const String& mimeType, const String& urlString, String& pluginPath)
424 {
425     String newMimeType = mimeType.lower();
426
427     PluginInfoStore::Plugin plugin = pluginInfoStore()->findPlugin(newMimeType, KURL(ParsedURLString, urlString));
428     if (!plugin.path)
429         return;
430
431     pluginPath = plugin.path;
432 }
433
434 uint64_t WebContext::createDownloadProxy()
435 {
436     RefPtr<DownloadProxy> downloadProxy = DownloadProxy::create(this);
437     uint64_t downloadID = downloadProxy->downloadID();
438
439     m_downloads.set(downloadID, downloadProxy.release());
440
441     return downloadID;
442 }
443
444 void WebContext::downloadFinished(DownloadProxy* downloadProxy)
445 {
446     ASSERT(m_downloads.contains(downloadProxy->downloadID()));
447
448     downloadProxy->invalidate();
449     m_downloads.remove(downloadProxy->downloadID());
450 }
451
452 // FIXME: This is not the ideal place for this function.
453 HashSet<String, CaseFoldingHash> WebContext::pdfAndPostScriptMIMETypes()
454 {
455     HashSet<String, CaseFoldingHash> mimeTypes;
456
457     mimeTypes.add("application/pdf");
458     mimeTypes.add("application/postscript");
459     mimeTypes.add("text/pdf");
460     
461     return mimeTypes;
462 }
463
464 void WebContext::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments)
465 {
466     if (messageID.is<CoreIPC::MessageClassWebContext>()) {
467         didReceiveWebContextMessage(connection, messageID, arguments);
468         return;
469     }
470
471     if (messageID.is<CoreIPC::MessageClassDownloadProxy>()) {
472         if (DownloadProxy* downloadProxy = m_downloads.get(arguments->destinationID()).get())
473             downloadProxy->didReceiveDownloadProxyMessage(connection, messageID, arguments);
474         
475         return;
476     }
477
478     if (messageID.is<CoreIPC::MessageClassWebDatabaseManagerProxy>()) {
479         m_databaseManagerProxy->didReceiveWebDatabaseManagerProxyMessage(connection, messageID, arguments);
480         return;
481     }
482
483     if (messageID.is<CoreIPC::MessageClassWebGeolocationManagerProxy>()) {
484         m_geolocationManagerProxy->didReceiveMessage(connection, messageID, arguments);
485         return;
486     }
487
488     switch (messageID.get<WebContextLegacyMessage::Kind>()) {
489         case WebContextLegacyMessage::PostMessage: {
490             String messageName;
491             RefPtr<APIObject> messageBody;
492             WebContextUserMessageDecoder messageDecoder(messageBody, this);
493             if (!arguments->decode(CoreIPC::Out(messageName, messageDecoder)))
494                 return;
495
496             didReceiveMessageFromInjectedBundle(messageName, messageBody.get());
497             return;
498         }
499         case WebContextLegacyMessage::PostSynchronousMessage:
500             ASSERT_NOT_REACHED();
501     }
502
503     ASSERT_NOT_REACHED();
504 }
505
506 CoreIPC::SyncReplyMode WebContext::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, CoreIPC::ArgumentEncoder* reply)
507 {
508     if (messageID.is<CoreIPC::MessageClassWebContext>())
509         return didReceiveSyncWebContextMessage(connection, messageID, arguments, reply);
510
511     if (messageID.is<CoreIPC::MessageClassDownloadProxy>()) {
512         if (DownloadProxy* downloadProxy = m_downloads.get(arguments->destinationID()).get())
513             return downloadProxy->didReceiveSyncDownloadProxyMessage(connection, messageID, arguments, reply);
514
515         return CoreIPC::AutomaticReply;
516     }
517     
518     switch (messageID.get<WebContextLegacyMessage::Kind>()) {
519         case WebContextLegacyMessage::PostSynchronousMessage: {
520             // FIXME: We should probably encode something in the case that the arguments do not decode correctly.
521
522             String messageName;
523             RefPtr<APIObject> messageBody;
524             WebContextUserMessageDecoder messageDecoder(messageBody, this);
525             if (!arguments->decode(CoreIPC::Out(messageName, messageDecoder)))
526                 return CoreIPC::AutomaticReply;
527
528             RefPtr<APIObject> returnData;
529             didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody.get(), returnData);
530             reply->encode(CoreIPC::In(WebContextUserMessageEncoder(returnData.get())));
531             return CoreIPC::AutomaticReply;
532         }
533         case WebContextLegacyMessage::PostMessage:
534             ASSERT_NOT_REACHED();
535     }
536
537     return CoreIPC::AutomaticReply;
538 }
539
540 void WebContext::clearResourceCaches()
541 {
542     if (!hasValidProcess()) {
543         // FIXME <rdar://problem/8727879>: Setting this flag ensures that the next time a WebProcess is created, this request to
544         // clear the resource cache will be respected. But if the user quits the application before another WebProcess is created,
545         // their request will be ignored.
546         m_clearResourceCachesForNewWebProcess = true;
547         return;
548     }
549
550     m_process->send(Messages::WebProcess::ClearResourceCaches(), 0);
551 }
552
553 void WebContext::clearApplicationCache()
554 {
555     if (!hasValidProcess()) {
556         // FIXME <rdar://problem/8727879>: Setting this flag ensures that the next time a WebProcess is created, this request to
557         // clear the application cache will be respected. But if the user quits the application before another WebProcess is created,
558         // their request will be ignored.
559         m_clearApplicationCacheForNewWebProcess = true;
560         return;
561     }
562
563     m_process->send(Messages::WebProcess::ClearApplicationCache(), 0);
564 }
565     
566 void WebContext::startMemorySampler(const double interval)
567 {    
568     // For new WebProcesses we will also want to start the Memory Sampler
569     m_memorySamplerEnabled = true;
570     m_memorySamplerInterval = interval;
571     
572     // For UIProcess
573 #if ENABLE(MEMORY_SAMPLER)
574     WebMemorySampler::shared()->start(interval);
575 #endif
576     
577     if (!hasValidProcess())
578         return;
579     
580     // For WebProcess
581     SandboxExtension::Handle sampleLogSandboxHandle;    
582     double now = WTF::currentTime();
583     String sampleLogFilePath = String::format("WebProcess%llu", static_cast<uint64_t>(now));
584     sampleLogFilePath = SandboxExtension::createHandleForTemporaryFile(sampleLogFilePath, SandboxExtension::WriteOnly, sampleLogSandboxHandle);
585     
586     m_process->send(Messages::WebProcess::StartMemorySampler(sampleLogSandboxHandle, sampleLogFilePath, interval), 0);
587 }
588
589 void WebContext::stopMemorySampler()
590 {    
591     // For WebProcess
592     m_memorySamplerEnabled = false;
593     
594     // For UIProcess
595 #if ENABLE(MEMORY_SAMPLER)
596     WebMemorySampler::shared()->stop();
597 #endif
598     
599     if (!hasValidProcess())
600         return;
601     
602     m_process->send(Messages::WebProcess::StopMemorySampler(), 0);
603 }
604
605 } // namespace WebKit