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.state = State::Finished;
254     m_uncommittedState.url = url;
255     m_uncommittedState.provisionalURL = String();
256 }
257
258 void PageLoadState::didStartProvisionalLoad(const Transaction::Token& token, const String& url, const String& unreachableURL)
259 {
260     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
261     ASSERT(m_uncommittedState.provisionalURL.isEmpty());
262
263     m_uncommittedState.state = State::Provisional;
264
265     m_uncommittedState.provisionalURL = url;
266
267     setUnreachableURL(token, unreachableURL);
268 }
269
270 void PageLoadState::didReceiveServerRedirectForProvisionalLoad(const Transaction::Token& token, const String& url)
271 {
272     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
273     ASSERT(m_uncommittedState.state == State::Provisional);
274
275     m_uncommittedState.provisionalURL = url;
276 }
277
278 void PageLoadState::didFailProvisionalLoad(const Transaction::Token& token)
279 {
280     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
281     ASSERT(m_uncommittedState.state == State::Provisional);
282
283     m_uncommittedState.state = State::Finished;
284
285     m_uncommittedState.provisionalURL = String();
286     m_uncommittedState.unreachableURL = m_lastUnreachableURL;
287 }
288
289 void PageLoadState::didCommitLoad(const Transaction::Token& token, WebCertificateInfo& certificateInfo, bool hasInsecureContent)
290 {
291     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
292     ASSERT(m_uncommittedState.state == State::Provisional);
293
294     m_uncommittedState.state = State::Committed;
295     m_uncommittedState.hasInsecureContent = hasInsecureContent;
296     m_uncommittedState.certificateInfo = &certificateInfo;
297
298     m_uncommittedState.url = m_uncommittedState.provisionalURL;
299     m_uncommittedState.provisionalURL = String();
300
301     m_uncommittedState.title = String();
302 }
303
304 void PageLoadState::didFinishLoad(const Transaction::Token& token)
305 {
306     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
307     ASSERT(m_uncommittedState.state == State::Committed);
308     ASSERT(m_uncommittedState.provisionalURL.isEmpty());
309
310     m_uncommittedState.state = State::Finished;
311 }
312
313 void PageLoadState::didFailLoad(const Transaction::Token& token)
314 {
315     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
316     ASSERT(m_uncommittedState.provisionalURL.isEmpty());
317
318     m_uncommittedState.state = State::Finished;
319 }
320
321 void PageLoadState::didSameDocumentNavigation(const Transaction::Token& token, const String& url)
322 {
323     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
324     ASSERT(!m_uncommittedState.url.isEmpty());
325
326     m_uncommittedState.url = url;
327 }
328
329 void PageLoadState::didDisplayOrRunInsecureContent(const Transaction::Token& token)
330 {
331     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
332
333     m_uncommittedState.hasInsecureContent = true;
334 }
335
336 void PageLoadState::setUnreachableURL(const Transaction::Token& token, const String& unreachableURL)
337 {
338     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
339
340     m_lastUnreachableURL = m_uncommittedState.unreachableURL;
341     m_uncommittedState.unreachableURL = unreachableURL;
342 }
343
344 const String& PageLoadState::title() const
345 {
346     return m_committedState.title;
347 }
348
349 void PageLoadState::setTitle(const Transaction::Token& token, const String& title)
350 {
351     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
352     m_uncommittedState.title = title;
353 }
354
355 bool PageLoadState::canGoBack() const
356 {
357     return m_committedState.canGoBack;
358 }
359
360 void PageLoadState::setCanGoBack(const Transaction::Token& token, bool canGoBack)
361 {
362     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
363     m_uncommittedState.canGoBack = canGoBack;
364 }
365
366 bool PageLoadState::canGoForward() const
367 {
368     return m_committedState.canGoForward;
369 }
370
371 void PageLoadState::setCanGoForward(const Transaction::Token& token, bool canGoForward)
372 {
373     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
374     m_uncommittedState.canGoForward = canGoForward;
375 }
376
377 void PageLoadState::didStartProgress(const Transaction::Token& token)
378 {
379     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
380     m_uncommittedState.estimatedProgress = initialProgressValue;
381 }
382
383 void PageLoadState::didChangeProgress(const Transaction::Token& token, double value)
384 {
385     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
386     m_uncommittedState.estimatedProgress = value;
387 }
388
389 void PageLoadState::didFinishProgress(const Transaction::Token& token)
390 {
391     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
392     m_uncommittedState.estimatedProgress = 1;
393 }
394
395 void PageLoadState::setNetworkRequestsInProgress(const Transaction::Token& token, bool networkRequestsInProgress)
396 {
397     ASSERT_UNUSED(token, &token.m_pageLoadState == this);
398     m_uncommittedState.networkRequestsInProgress = networkRequestsInProgress;
399 }
400
401 bool PageLoadState::isLoading(const Data& data)
402 {
403     if (!data.pendingAPIRequestURL.isNull())
404         return true;
405
406     switch (data.state) {
407     case State::Provisional:
408     case State::Committed:
409         return true;
410
411     case State::Finished:
412         return false;
413     }
414
415     ASSERT_NOT_REACHED();
416     return false;
417 }
418
419 void PageLoadState::didSwapWebProcesses()
420 {
421     callObserverCallback(&Observer::didSwapWebProcesses);
422 }
423
424 void PageLoadState::willChangeProcessIsResponsive()
425 {
426     callObserverCallback(&Observer::willChangeWebProcessIsResponsive);
427 }
428
429 void PageLoadState::didChangeProcessIsResponsive()
430 {
431     callObserverCallback(&Observer::didChangeWebProcessIsResponsive);
432 }
433
434 void PageLoadState::callObserverCallback(void (Observer::*callback)())
435 {
436     auto protectedPage = makeRef(m_webPageProxy);
437
438     auto observerCopy = m_observers;
439     for (auto* observer : observerCopy) {
440         // This appears potentially inefficient on the surface (searching in a Vector)
441         // but in practice - using only API - there will only ever be (1) observer.
442         if (!m_observers.contains(observer))
443             continue;
444
445         (observer->*callback)();
446     }
447 }
448
449 } // namespace WebKit