[PSON] Introduce a WebContent Process cache
[WebKit-https.git] / Source / WebKit / UIProcess / ProvisionalPageProxy.cpp
1 /*
2  * Copyright (C) 2019 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 "ProvisionalPageProxy.h"
28
29 #include "APINavigation.h"
30 #include "DrawingAreaProxy.h"
31 #include "FormDataReference.h"
32 #include "Logging.h"
33 #include "PageClient.h"
34 #include "URLSchemeTaskParameters.h"
35 #include "WebBackForwardList.h"
36 #include "WebBackForwardListItem.h"
37 #include "WebErrors.h"
38 #include "WebNavigationDataStore.h"
39 #include "WebNavigationState.h"
40 #include "WebPageMessages.h"
41 #include "WebPageProxy.h"
42 #include "WebPageProxyMessages.h"
43 #include "WebProcessMessages.h"
44 #include "WebProcessProxy.h"
45 #include <WebCore/ShouldTreatAsContinuingLoad.h>
46
47 namespace WebKit {
48
49 #define RELEASE_LOG_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_IF(m_page.isAlwaysOnLoggingAllowed(), channel, "%p - ProvisionalPageProxy::" fmt, this, ##__VA_ARGS__)
50 #define RELEASE_LOG_ERROR_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_ERROR_IF(m_page.isAlwaysOnLoggingAllowed(), channel, "%p - ProvisionalPageProxy::" fmt, this, ##__VA_ARGS__)
51
52 ProvisionalPageProxy::ProvisionalPageProxy(WebPageProxy& page, Ref<WebProcessProxy>&& process, std::unique_ptr<SuspendedPageProxy> suspendedPage, uint64_t navigationID, bool isServerRedirect, const WebCore::ResourceRequest& request, ProcessSwapRequestedByClient processSwapRequestedByClient)
53     : m_page(page)
54     , m_process(WTFMove(process))
55     , m_navigationID(navigationID)
56     , m_isServerRedirect(isServerRedirect)
57     , m_request(request)
58     , m_processSwapRequestedByClient(processSwapRequestedByClient)
59 #if PLATFORM(IOS_FAMILY)
60     , m_suspensionToken(m_process->throttler().foregroundActivityToken())
61 #endif
62 {
63     m_process->addMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_page.pageID(), *this);
64     m_process->addProvisionalPageProxy(*this);
65
66     // If we are reattaching to a SuspendedPage, then the WebProcess' WebPage already exists and
67     // WebPageProxy::didCreateMainFrame() will not be called to initialize m_mainFrame. In such
68     // case, we need to initialize m_mainFrame to reflect the fact the the WebProcess' WebPage
69     // already exists and already has a main frame.
70     if (suspendedPage) {
71         ASSERT(&suspendedPage->process() == m_process.ptr());
72         suspendedPage->unsuspend();
73         m_mainFrame = WebFrameProxy::create(m_page, suspendedPage->mainFrameID());
74         m_process->frameCreated(suspendedPage->mainFrameID(), *m_mainFrame);
75     }
76
77     initializeWebPage();
78 }
79
80 ProvisionalPageProxy::~ProvisionalPageProxy()
81 {
82     m_process->removeProvisionalPageProxy(*this);
83
84     if (m_wasCommitted)
85         return;
86
87     m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_page.pageID());
88     m_process->send(Messages::WebPage::Close(), m_page.pageID());
89
90     RunLoop::main().dispatch([process = m_process.copyRef()] {
91         process->maybeShutDown();
92     });
93 }
94
95 void ProvisionalPageProxy::processDidTerminate()
96 {
97     RELEASE_LOG_ERROR_IF_ALLOWED(ProcessSwapping, "processDidTerminate: pageID = %" PRIu64, m_page.pageID());
98     m_page.provisionalProcessDidTerminate();
99 }
100
101 std::unique_ptr<DrawingAreaProxy> ProvisionalPageProxy::takeDrawingArea()
102 {
103     return WTFMove(m_drawingArea);
104 }
105
106 void ProvisionalPageProxy::cancel()
107 {
108     // If the provisional load started, then indicate that it failed due to cancellation by calling didFailProvisionalLoadForFrame().
109     if (m_provisionalLoadURL.isEmpty())
110         return;
111
112     RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "cancel: Simulating a didFailProvisionalLoadForFrame for pageID = %" PRIu64, m_page.pageID());
113     ASSERT(m_mainFrame);
114     auto error = WebKit::cancelledError(m_request);
115     error.setType(WebCore::ResourceError::Type::Cancellation);
116     didFailProvisionalLoadForFrame(m_mainFrame->frameID(), { }, m_navigationID, m_provisionalLoadURL, error, UserData { }); // Will delete |this|.
117 }
118
119 void ProvisionalPageProxy::processDidFinishLaunching()
120 {
121     RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "processDidFinishLaunching: pageID = %" PRIu64, m_page.pageID());
122     finishInitializingWebPageAfterProcessLaunch();
123 }
124
125 void ProvisionalPageProxy::finishInitializingWebPageAfterProcessLaunch()
126 {
127     ASSERT(m_process->state() == WebProcessProxy::State::Running);
128
129     // FIXME: The WebPageProxy delays adding the visited link store until after the process has launched
130     // so the ProvisionalPageProxy does the same. However, do we really need to?
131     m_process->addVisitedLinkStore(m_page.visitedLinkStore());
132 }
133
134 void ProvisionalPageProxy::initializeWebPage()
135 {
136     m_drawingArea = m_page.pageClient().createDrawingAreaProxy(m_process);
137
138     auto parameters = m_page.creationParameters(m_process);
139     parameters.isProcessSwap = true;
140     m_process->send(Messages::WebProcess::CreateWebPage(m_page.pageID(), parameters), 0);
141
142     if (m_process->state() == WebProcessProxy::State::Running)
143         finishInitializingWebPageAfterProcessLaunch();
144 }
145
146 void ProvisionalPageProxy::loadData(API::Navigation& navigation, const IPC::DataReference& data, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData, Optional<WebsitePoliciesData>&& websitePolicies)
147 {
148     RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "loadData: pageID = %" PRIu64, m_page.pageID());
149
150     m_page.loadDataWithNavigationShared(m_process.copyRef(), navigation, data, MIMEType, encoding, baseURL, userData, WebCore::ShouldTreatAsContinuingLoad::Yes, WTFMove(websitePolicies));
151 }
152
153 void ProvisionalPageProxy::loadRequest(API::Navigation& navigation, WebCore::ResourceRequest&& request, WebCore::ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, API::Object* userData, Optional<WebsitePoliciesData>&& websitePolicies)
154 {
155     RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "loadRequest: pageID = %" PRIu64, m_page.pageID());
156
157     // If this is a client-side redirect continuing in a new process, then the new process will overwrite the fromItem's URL. In this case,
158     // we need to make sure we update fromItem's processIdentifier as we want future navigations to this BackForward item to happen in the
159     // new process.
160     if (navigation.fromItem() && navigation.lockBackForwardList() == WebCore::LockBackForwardList::Yes)
161         navigation.fromItem()->setLastProcessIdentifier(m_process->coreProcessIdentifier());
162
163     m_page.loadRequestWithNavigationShared(m_process.copyRef(), navigation, WTFMove(request), shouldOpenExternalURLsPolicy, userData, WebCore::ShouldTreatAsContinuingLoad::Yes, WTFMove(websitePolicies));
164 }
165
166 void ProvisionalPageProxy::goToBackForwardItem(API::Navigation& navigation, WebBackForwardListItem& item, Optional<WebsitePoliciesData>&& websitePolicies)
167 {
168     RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "goToBackForwardItem: pageID = %" PRIu64, m_page.pageID());
169
170     auto itemStates = m_page.backForwardList().filteredItemStates([this, targetItem = &item](auto& item) {
171         if (auto* page = item.suspendedPage()) {
172             if (&page->process() == m_process.ptr())
173                 return false;
174         }
175         return &item != targetItem;
176     });
177     m_process->send(Messages::WebPage::UpdateBackForwardListForReattach(WTFMove(itemStates)), m_page.pageID());
178     m_process->send(Messages::WebPage::GoToBackForwardItem(navigation.navigationID(), item.itemID(), *navigation.backForwardFrameLoadType(), WebCore::ShouldTreatAsContinuingLoad::Yes, WTFMove(websitePolicies)), m_page.pageID());
179     m_process->responsivenessTimer().start();
180 }
181
182 void ProvisionalPageProxy::didCreateMainFrame(uint64_t frameID)
183 {
184     RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "didCreateMainFrame: pageID = %" PRIu64 ", frameID = %" PRIu64, m_page.pageID(), frameID);
185     ASSERT(!m_mainFrame);
186
187     m_mainFrame = WebFrameProxy::create(m_page, frameID);
188
189     // Add the frame to the process wide map.
190     m_process->frameCreated(frameID, *m_mainFrame);
191
192     // This navigation was destroyed so no need to notify of redirect.
193     if (!m_page.navigationState().hasNavigation(m_navigationID))
194         return;
195
196     // Restore the main frame's committed URL as some clients may rely on it until the next load is committed.
197     if (auto* mainFrame = m_page.mainFrame())
198         m_mainFrame->frameLoadState().setURL(mainFrame->frameLoadState().url());
199
200     // Normally, notification of a server redirect comes from the WebContent process.
201     // If we are process swapping in response to a server redirect then that notification will not come from the new WebContent process.
202     // In this case we have the UIProcess synthesize the redirect notification at the appropriate time.
203     if (m_isServerRedirect) {
204         m_mainFrame->frameLoadState().didStartProvisionalLoad(m_request.url());
205         m_page.didReceiveServerRedirectForProvisionalLoadForFrameShared(m_process.copyRef(), m_mainFrame->frameID(), m_navigationID, WTFMove(m_request), { });
206     }
207 }
208
209 void ProvisionalPageProxy::didPerformClientRedirect(const String& sourceURLString, const String& destinationURLString, uint64_t frameID)
210 {
211     m_page.didPerformClientRedirectShared(m_process.copyRef(), sourceURLString, destinationURLString, frameID);
212 }
213
214 void ProvisionalPageProxy::didStartProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, URL&& url, URL&& unreachableURL, const UserData& userData)
215 {
216     // If the previous provisional load used the same process, we may receive IPC for this previous provisional's main frame that we need to ignore.
217     if (!m_mainFrame || m_mainFrame->frameID() != frameID)
218         return;
219
220     RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "didStartProvisionalLoadForFrame: pageID = %" PRIu64 ", frameID = %" PRIu64 ", navigationID = %" PRIu64, m_page.pageID(), frameID, navigationID);
221     ASSERT(m_provisionalLoadURL.isNull());
222     m_provisionalLoadURL = url;
223
224     // Merely following a server side redirect so there is no need to send a didStartProvisionalLoad again.
225     if (m_isServerRedirect)
226         return;
227
228     // Clients expect the Page's main frame's expectedURL to be the provisional one when a provisional load is started.
229     if (auto* pageMainFrame = m_page.mainFrame())
230         pageMainFrame->didStartProvisionalLoad(url);
231
232     m_page.didStartProvisionalLoadForFrameShared(m_process.copyRef(), frameID, navigationID, WTFMove(url), WTFMove(unreachableURL), userData);
233 }
234
235 void ProvisionalPageProxy::didFailProvisionalLoadForFrame(uint64_t frameID, const WebCore::SecurityOriginData& frameSecurityOrigin, uint64_t navigationID, const String& provisionalURL, const WebCore::ResourceError& error, const UserData& userData)
236 {
237     RELEASE_LOG_ERROR_IF_ALLOWED(ProcessSwapping, "didFailProvisionalLoadForFrame: pageID = %" PRIu64 ", frameID = %" PRIu64 ", navigationID = %" PRIu64, m_page.pageID(), frameID, navigationID);
238     m_provisionalLoadURL = { };
239
240     // Make sure the Page's main frame's expectedURL gets cleared since we updated it in didStartProvisionalLoad.
241     if (auto* pageMainFrame = m_page.mainFrame())
242         pageMainFrame->didFailProvisionalLoad();
243
244     m_page.didFailProvisionalLoadForFrameShared(m_process.copyRef(), frameID, frameSecurityOrigin, navigationID, provisionalURL, error, userData); // Will delete |this|.
245 }
246
247 void ProvisionalPageProxy::didCommitLoadForFrame(uint64_t frameID, uint64_t navigationID, const String& mimeType, bool frameHasCustomContentProvider, uint32_t frameLoadType, const WebCore::CertificateInfo& certificateInfo, bool containsPluginDocument, Optional<WebCore::HasInsecureContent> forcedHasInsecureContent, const UserData& userData)
248 {
249     // If the previous provisional load used the same process, we may receive IPC for this previous provisional's main frame that we need to ignore.
250     if (!m_mainFrame || m_mainFrame->frameID() != frameID)
251         return;
252
253     RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "didCommitLoadForFrame: pageID = %" PRIu64 ", frameID = %" PRIu64 ", navigationID = %" PRIu64, m_page.pageID(), frameID, navigationID);
254     m_provisionalLoadURL = { };
255     m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_page.pageID());
256
257     m_wasCommitted = true;
258     m_page.commitProvisionalPage(frameID, navigationID, mimeType, frameHasCustomContentProvider, frameLoadType, certificateInfo, containsPluginDocument, forcedHasInsecureContent, userData); // Will delete |this|.
259 }
260
261 void ProvisionalPageProxy::didNavigateWithNavigationData(const WebNavigationDataStore& store, uint64_t frameID)
262 {
263     m_page.didNavigateWithNavigationDataShared(m_process.copyRef(), store, frameID);
264 }
265
266 void ProvisionalPageProxy::didChangeProvisionalURLForFrame(uint64_t frameID, uint64_t navigationID, URL&& url)
267 {
268     m_page.didChangeProvisionalURLForFrameShared(m_process.copyRef(), frameID, navigationID, WTFMove(url));
269 }
270
271 void ProvisionalPageProxy::decidePolicyForNavigationActionAsync(uint64_t frameID, WebCore::SecurityOriginData&& frameSecurityOrigin, WebCore::PolicyCheckIdentifier identifier,
272     uint64_t navigationID, NavigationActionData&& navigationActionData, FrameInfoData&& frameInfoData, uint64_t originatingPageID, const WebCore::ResourceRequest& originalRequest,
273     WebCore::ResourceRequest&& request, IPC::FormDataReference&& requestBody, WebCore::ResourceResponse&& redirectResponse, const UserData& userData, uint64_t listenerID)
274 {
275     ASSERT(m_mainFrame);
276     ASSERT(m_mainFrame->frameID() == frameID);
277     m_page.decidePolicyForNavigationActionAsyncShared(m_process.copyRef(), frameID, WTFMove(frameSecurityOrigin), identifier, navigationID, WTFMove(navigationActionData),
278         WTFMove(frameInfoData), originatingPageID, originalRequest, WTFMove(request), WTFMove(requestBody), WTFMove(redirectResponse), userData, listenerID);
279 }
280
281 void ProvisionalPageProxy::decidePolicyForResponse(uint64_t frameID, const WebCore::SecurityOriginData& frameSecurityOrigin, WebCore::PolicyCheckIdentifier identifier,
282     uint64_t navigationID, const WebCore::ResourceResponse& response, const WebCore::ResourceRequest& request, bool canShowMIMEType, uint64_t listenerID, const UserData& userData)
283 {
284     m_page.decidePolicyForResponseShared(m_process.copyRef(), frameID, frameSecurityOrigin, identifier, navigationID, response, request, canShowMIMEType, listenerID, userData);
285 }
286
287 void ProvisionalPageProxy::startURLSchemeTask(URLSchemeTaskParameters&& parameters)
288 {
289     m_page.startURLSchemeTaskShared(m_process.copyRef(), WTFMove(parameters));
290 }
291
292 void ProvisionalPageProxy::backForwardGoToItem(const WebCore::BackForwardItemIdentifier& identifier, SandboxExtension::Handle& handle)
293 {
294     m_page.backForwardGoToItemShared(m_process.copyRef(), identifier, handle);
295 }
296
297 #if PLATFORM(COCOA)
298 void ProvisionalPageProxy::registerWebProcessAccessibilityToken(const IPC::DataReference& data)
299 {
300     m_accessibilityToken = data.vector();
301 }
302 #endif
303
304 void ProvisionalPageProxy::didReceiveMessage(IPC::Connection& connection, IPC::Decoder& decoder)
305 {
306     ASSERT(decoder.messageReceiverName() == Messages::WebPageProxy::messageReceiverName());
307
308     if (decoder.messageName() == Messages::WebPageProxy::DidStartProgress::name()
309         || decoder.messageName() == Messages::WebPageProxy::DidChangeProgress::name()
310         || decoder.messageName() == Messages::WebPageProxy::BackForwardAddItem::name()
311         || decoder.messageName() == Messages::WebPageProxy::LogDiagnosticMessage::name()
312         || decoder.messageName() == Messages::WebPageProxy::LogDiagnosticMessageWithEnhancedPrivacy::name()
313         || decoder.messageName() == Messages::WebPageProxy::SetNetworkRequestsInProgress::name()
314         )
315     {
316         m_page.didReceiveMessage(connection, decoder);
317         return;
318     }
319
320 #if PLATFORM(COCOA)
321     if (decoder.messageName() == Messages::WebPageProxy::RegisterWebProcessAccessibilityToken::name()) {
322         IPC::handleMessage<Messages::WebPageProxy::RegisterWebProcessAccessibilityToken>(decoder, this, &ProvisionalPageProxy::registerWebProcessAccessibilityToken);
323         return;
324     }
325 #endif
326
327     if (decoder.messageName() == Messages::WebPageProxy::StartURLSchemeTask::name()) {
328         IPC::handleMessage<Messages::WebPageProxy::StartURLSchemeTask>(decoder, this, &ProvisionalPageProxy::startURLSchemeTask);
329         return;
330     }
331
332     if (decoder.messageName() == Messages::WebPageProxy::DecidePolicyForNavigationActionAsync::name()) {
333         IPC::handleMessage<Messages::WebPageProxy::DecidePolicyForNavigationActionAsync>(decoder, this, &ProvisionalPageProxy::decidePolicyForNavigationActionAsync);
334         return;
335     }
336
337     if (decoder.messageName() == Messages::WebPageProxy::DecidePolicyForResponse::name()) {
338         IPC::handleMessage<Messages::WebPageProxy::DecidePolicyForResponse>(decoder, this, &ProvisionalPageProxy::decidePolicyForResponse);
339         return;
340     }
341
342     if (decoder.messageName() == Messages::WebPageProxy::DidChangeProvisionalURLForFrame::name()) {
343         IPC::handleMessage<Messages::WebPageProxy::DidChangeProvisionalURLForFrame>(decoder, this, &ProvisionalPageProxy::didChangeProvisionalURLForFrame);
344         return;
345     }
346
347     if (decoder.messageName() == Messages::WebPageProxy::DidNavigateWithNavigationData::name()) {
348         IPC::handleMessage<Messages::WebPageProxy::DidNavigateWithNavigationData>(decoder, this, &ProvisionalPageProxy::didNavigateWithNavigationData);
349         return;
350     }
351
352     if (decoder.messageName() == Messages::WebPageProxy::DidPerformClientRedirect::name()) {
353         IPC::handleMessage<Messages::WebPageProxy::DidPerformClientRedirect>(decoder, this, &ProvisionalPageProxy::didPerformClientRedirect);
354         return;
355     }
356
357     if (decoder.messageName() == Messages::WebPageProxy::DidCreateMainFrame::name()) {
358         IPC::handleMessage<Messages::WebPageProxy::DidCreateMainFrame>(decoder, this, &ProvisionalPageProxy::didCreateMainFrame);
359         return;
360     }
361
362     if (decoder.messageName() == Messages::WebPageProxy::DidStartProvisionalLoadForFrame::name()) {
363         IPC::handleMessage<Messages::WebPageProxy::DidStartProvisionalLoadForFrame>(decoder, this, &ProvisionalPageProxy::didStartProvisionalLoadForFrame);
364         return;
365     }
366
367     if (decoder.messageName() == Messages::WebPageProxy::DidFailProvisionalLoadForFrame::name()) {
368         IPC::handleMessage<Messages::WebPageProxy::DidFailProvisionalLoadForFrame>(decoder, this, &ProvisionalPageProxy::didFailProvisionalLoadForFrame);
369         return;
370     }
371
372     if (decoder.messageName() == Messages::WebPageProxy::DidCommitLoadForFrame::name()) {
373         IPC::handleMessage<Messages::WebPageProxy::DidCommitLoadForFrame>(decoder, this, &ProvisionalPageProxy::didCommitLoadForFrame);
374         return;
375     }
376
377     LOG(ProcessSwapping, "Unhandled message %s::%s from provisional process", decoder.messageReceiverName().toString().data(), decoder.messageName().toString().data());
378 }
379
380 void ProvisionalPageProxy::didReceiveSyncMessage(IPC::Connection& connection, IPC::Decoder& decoder, std::unique_ptr<IPC::Encoder>& replyEncoder)
381 {
382     if (decoder.messageName() == Messages::WebPageProxy::BackForwardGoToItem::name()) {
383         IPC::handleMessageLegacySync<Messages::WebPageProxy::BackForwardGoToItem>(decoder, *replyEncoder, this, &ProvisionalPageProxy::backForwardGoToItem);
384         return;
385     }
386
387     m_page.didReceiveSyncMessage(connection, decoder, replyEncoder);
388 }
389
390 } // namespace WebKit