URL set by document.open() is not communicated to the UIProcess
[WebKit-https.git] / Source / WebKit / UIProcess / PageLoadState.cpp
1 /*
2  * Copyright (C) 2013-2015 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 "PageLoadState.h"
28
29 #include "WebPageProxy.h"
30
31 namespace WebKit {
32
33 // Progress always starts at this value. This helps provide feedback as soon as a load starts.
34 static const double initialProgressValue = 0.1;
35
36 PageLoadState::PageLoadState(WebPageProxy& webPageProxy)
37     : m_webPageProxy(webPageProxy)
38     , m_mayHaveUncommittedChanges(false)
39     , m_outstandingTransactionCount(0)
40 {
41 }
42
43 PageLoadState::~PageLoadState()
44 {
45     ASSERT(m_observers.isEmpty());
46 }
47
48 PageLoadState::Transaction::Transaction(PageLoadState& pageLoadState)
49     : m_webPageProxy(&pageLoadState.m_webPageProxy)
50     , m_pageLoadState(&pageLoadState)
51 {
52     m_pageLoadState->beginTransaction();
53 }
54
55 PageLoadState::Transaction::Transaction(Transaction&& other)
56     : m_webPageProxy(WTFMove(other.m_webPageProxy))
57     , m_pageLoadState(other.m_pageLoadState)
58 {
59     other.m_pageLoadState = nullptr;
60 }
61
62 PageLoadState::Transaction::~Transaction()
63 {
64     if (m_pageLoadState)
65         m_pageLoadState->endTransaction();
66 }
67
68 void PageLoadState::addObserver(Observer& observer)
69 {
70     ASSERT(!m_observers.contains(&observer));
71
72     m_observers.append(&observer);
73 }
74
75 void PageLoadState::removeObserver(Observer& observer)
76 {
77     bool removed = m_observers.removeFirst(&observer);
78     ASSERT_UNUSED(removed, removed);
79 }
80
81 void PageLoadState::endTransaction()
82 {
83     ASSERT(m_outstandingTransactionCount > 0);
84
85     if (!--m_outstandingTransactionCount)
86         commitChanges();
87 }
88
89 void PageLoadState::commitChanges()
90 {
91     if (!m_mayHaveUncommittedChanges)
92         return;
93
94     m_mayHaveUncommittedChanges = false;
95
96     bool canGoBackChanged = m_committedState.canGoBack != m_uncommittedState.canGoBack;
97     bool canGoForwardChanged = m_committedState.canGoForward != m_uncommittedState.canGoForward;
98     bool titleChanged = m_committedState.title != m_uncommittedState.title;
99     bool isLoadingChanged = isLoading(m_committedState) != isLoading(m_uncommittedState);
100     bool activeURLChanged = activeURL(m_committedState) != activeURL(m_uncommittedState);
101     bool hasOnlySecureContentChanged = hasOnlySecureContent(m_committedState) != hasOnlySecureContent(m_uncommittedState);
102     bool estimatedProgressChanged = estimatedProgress(m_committedState) != estimatedProgress(m_uncommittedState);
103     bool networkRequestsInProgressChanged = m_committedState.networkRequestsInProgress != m_uncommittedState.networkRequestsInProgress;
104     bool certificateInfoChanged = m_committedState.certificateInfo != m_uncommittedState.certificateInfo;
105
106     if (canGoBackChanged)
107         callObserverCallback(&Observer::willChangeCanGoBack);
108     if (canGoForwardChanged)
109         callObserverCallback(&Observer::willChangeCanGoForward);
110     if (titleChanged)
111         callObserverCallback(&Observer::willChangeTitle);
112     if (isLoadingChanged)
113         callObserverCallback(&Observer::willChangeIsLoading);
114     if (activeURLChanged)
115         callObserverCallback(&Observer::willChangeActiveURL);
116     if (hasOnlySecureContentChanged)
117         callObserverCallback(&Observer::willChangeHasOnlySecureContent);
118     if (estimatedProgressChanged)
119         callObserverCallback(&Observer::willChangeEstimatedProgress);
120     if (networkRequestsInProgressChanged)
121         callObserverCallback(&Observer::willChangeNetworkRequestsInProgress);
122     if (certificateInfoChanged)
123         callObserverCallback(&Observer::willChangeCertificateInfo);
124
125     m_committedState = m_uncommittedState;
126
127     m_webPageProxy.isLoadingChanged();
128
129     // The "did" ordering is the reverse of the "will". This is a requirement of Cocoa Key-Value Observing.
130     if (certificateInfoChanged)
131         callObserverCallback(&Observer::didChangeCertificateInfo);
132     if (networkRequestsInProgressChanged)
133         callObserverCallback(&Observer::didChangeNetworkRequestsInProgress);
134     if (estimatedProgressChanged)
135         callObserverCallback(&Observer::didChangeEstimatedProgress);
136     if (hasOnlySecureContentChanged)
137         callObserverCallback(&Observer::didChangeHasOnlySecureContent);
138     if (activeURLChanged)
139         callObserverCallback(&Observer::didChangeActiveURL);
140     if (isLoadingChanged)
141         callObserverCallback(&Observer::didChangeIsLoading);
142     if (titleChanged)
143         callObserverCallback(&Observer::didChangeTitle);
144     if (canGoForwardChanged)
145         callObserverCallback(&Observer::didChangeCanGoForward);
146     if (canGoBackChanged)
147         callObserverCallback(&Observer::didChangeCanGoBack);
148 }
149
150 void PageLoadState::reset(const Transaction::Token& token)
151 {
152     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
153
154     m_uncommittedState.state = State::Finished;
155     m_uncommittedState.hasInsecureContent = false;
156
157     m_uncommittedState.pendingAPIRequestURL = String();
158     m_uncommittedState.provisionalURL = String();
159     m_uncommittedState.url = String();
160
161     m_uncommittedState.unreachableURL = String();
162     m_lastUnreachableURL = String();
163
164     m_uncommittedState.title = String();
165
166     m_uncommittedState.estimatedProgress = 0;
167     m_uncommittedState.networkRequestsInProgress = false;
168 }
169
170 bool PageLoadState::isLoading() const
171 {
172     return isLoading(m_committedState);
173 }
174
175 String PageLoadState::activeURL(const Data& data)
176 {
177     // If there is a currently pending URL, it is the active URL,
178     // even when there's no main frame yet, as it might be the
179     // first API request.
180     if (!data.pendingAPIRequestURL.isNull())
181         return data.pendingAPIRequestURL;
182
183     if (!data.unreachableURL.isEmpty())
184         return data.unreachableURL;
185
186     switch (data.state) {
187     case State::Provisional:
188         return data.provisionalURL;
189     case State::Committed:
190     case State::Finished:
191         return data.url;
192     }
193
194     ASSERT_NOT_REACHED();
195     return String();
196 }
197
198 String PageLoadState::activeURL() const
199 {
200     return activeURL(m_committedState);
201 }
202
203 bool PageLoadState::hasOnlySecureContent(const Data& data)
204 {
205     if (data.hasInsecureContent)
206         return false;
207
208     if (data.state == State::Provisional)
209         return WTF::protocolIs(data.provisionalURL, "https");
210
211     return WTF::protocolIs(data.url, "https");
212 }
213
214 bool PageLoadState::hasOnlySecureContent() const
215 {
216     return hasOnlySecureContent(m_committedState);
217 }
218
219 double PageLoadState::estimatedProgress(const Data& data)
220 {
221     if (!data.pendingAPIRequestURL.isNull())
222         return initialProgressValue;
223
224     return data.estimatedProgress;
225 }
226
227 double PageLoadState::estimatedProgress() const
228 {
229     return estimatedProgress(m_committedState);
230 }
231
232 const String& PageLoadState::pendingAPIRequestURL() const
233 {
234     return m_committedState.pendingAPIRequestURL;
235 }
236
237 void PageLoadState::setPendingAPIRequestURL(const Transaction::Token& token, const String& pendingAPIRequestURL)
238 {
239     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
240     m_uncommittedState.pendingAPIRequestURL = pendingAPIRequestURL;
241 }
242
243 void PageLoadState::clearPendingAPIRequestURL(const Transaction::Token& token)
244 {
245     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
246     m_uncommittedState.pendingAPIRequestURL = String();
247 }
248
249 void PageLoadState::didExplicitOpen(const Transaction::Token& token, const String& url)
250 {
251     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
252
253     m_uncommittedState.url = url;
254     m_uncommittedState.provisionalURL = String();
255 }
256
257 void PageLoadState::didStartProvisionalLoad(const Transaction::Token& token, const String& url, const String& unreachableURL)
258 {
259     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
260     ASSERT(m_uncommittedState.provisionalURL.isEmpty());
261
262     m_uncommittedState.state = State::Provisional;
263
264     m_uncommittedState.provisionalURL = url;
265
266     setUnreachableURL(token, unreachableURL);
267 }
268
269 void PageLoadState::didReceiveServerRedirectForProvisionalLoad(const Transaction::Token& token, const String& url)
270 {
271     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
272     ASSERT(m_uncommittedState.state == State::Provisional);
273
274     m_uncommittedState.provisionalURL = url;
275 }
276
277 void PageLoadState::didFailProvisionalLoad(const Transaction::Token& token)
278 {
279     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
280     ASSERT(m_uncommittedState.state == State::Provisional);
281
282     m_uncommittedState.state = State::Finished;
283
284     m_uncommittedState.provisionalURL = String();
285     m_uncommittedState.unreachableURL = m_lastUnreachableURL;
286 }
287
288 void PageLoadState::didCommitLoad(const Transaction::Token& token, WebCertificateInfo& certificateInfo, bool hasInsecureContent)
289 {
290     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
291     ASSERT(m_uncommittedState.state == State::Provisional);
292
293     m_uncommittedState.state = State::Committed;
294     m_uncommittedState.hasInsecureContent = hasInsecureContent;
295     m_uncommittedState.certificateInfo = &certificateInfo;
296
297     m_uncommittedState.url = m_uncommittedState.provisionalURL;
298     m_uncommittedState.provisionalURL = String();
299
300     m_uncommittedState.title = String();
301 }
302
303 void PageLoadState::didFinishLoad(const Transaction::Token& token)
304 {
305     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
306     ASSERT(m_uncommittedState.state == State::Committed);
307     ASSERT(m_uncommittedState.provisionalURL.isEmpty());
308
309     m_uncommittedState.state = State::Finished;
310 }
311
312 void PageLoadState::didFailLoad(const Transaction::Token& token)
313 {
314     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
315     ASSERT(m_uncommittedState.provisionalURL.isEmpty());
316
317     m_uncommittedState.state = State::Finished;
318 }
319
320 void PageLoadState::didSameDocumentNavigation(const Transaction::Token& token, const String& url)
321 {
322     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
323     ASSERT(!m_uncommittedState.url.isEmpty());
324
325     m_uncommittedState.url = url;
326 }
327
328 void PageLoadState::didDisplayOrRunInsecureContent(const Transaction::Token& token)
329 {
330     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
331
332     m_uncommittedState.hasInsecureContent = true;
333 }
334
335 void PageLoadState::setUnreachableURL(const Transaction::Token& token, const String& unreachableURL)
336 {
337     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
338
339     m_lastUnreachableURL = m_uncommittedState.unreachableURL;
340     m_uncommittedState.unreachableURL = unreachableURL;
341 }
342
343 const String& PageLoadState::title() const
344 {
345     return m_committedState.title;
346 }
347
348 void PageLoadState::setTitle(const Transaction::Token& token, const String& title)
349 {
350     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
351     m_uncommittedState.title = title;
352 }
353
354 bool PageLoadState::canGoBack() const
355 {
356     return m_committedState.canGoBack;
357 }
358
359 void PageLoadState::setCanGoBack(const Transaction::Token& token, bool canGoBack)
360 {
361     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
362     m_uncommittedState.canGoBack = canGoBack;
363 }
364
365 bool PageLoadState::canGoForward() const
366 {
367     return m_committedState.canGoForward;
368 }
369
370 void PageLoadState::setCanGoForward(const Transaction::Token& token, bool canGoForward)
371 {
372     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
373     m_uncommittedState.canGoForward = canGoForward;
374 }
375
376 void PageLoadState::didStartProgress(const Transaction::Token& token)
377 {
378     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
379     m_uncommittedState.estimatedProgress = initialProgressValue;
380 }
381
382 void PageLoadState::didChangeProgress(const Transaction::Token& token, double value)
383 {
384     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
385     m_uncommittedState.estimatedProgress = value;
386 }
387
388 void PageLoadState::didFinishProgress(const Transaction::Token& token)
389 {
390     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
391     m_uncommittedState.estimatedProgress = 1;
392 }
393
394 void PageLoadState::setNetworkRequestsInProgress(const Transaction::Token& token, bool networkRequestsInProgress)
395 {
396     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
397     m_uncommittedState.networkRequestsInProgress = networkRequestsInProgress;
398 }
399
400 bool PageLoadState::isLoading(const Data& data)
401 {
402     if (!data.pendingAPIRequestURL.isNull())
403         return true;
404
405     switch (data.state) {
406     case State::Provisional:
407     case State::Committed:
408         return true;
409
410     case State::Finished:
411         return false;
412     }
413
414     ASSERT_NOT_REACHED();
415     return false;
416 }
417
418 void PageLoadState::didSwapWebProcesses()
419 {
420     callObserverCallback(&Observer::didSwapWebProcesses);
421 }
422
423 void PageLoadState::willChangeProcessIsResponsive()
424 {
425     callObserverCallback(&Observer::willChangeWebProcessIsResponsive);
426 }
427
428 void PageLoadState::didChangeProcessIsResponsive()
429 {
430     callObserverCallback(&Observer::didChangeWebProcessIsResponsive);
431 }
432
433 void PageLoadState::callObserverCallback(void (Observer::*callback)())
434 {
435     auto protectedPage = makeRef(m_webPageProxy);
436
437     auto observerCopy = m_observers;
438     for (auto* observer : observerCopy) {
439         // This appears potentially inefficient on the surface (searching in a Vector)
440         // but in practice - using only API - there will only ever be (1) observer.
441         if (!m_observers.contains(observer))
442             continue;
443
444         (observer->*callback)();
445     }
446 }
447
448 } // namespace WebKit