blob: d4044165c96b4f762630eaab4c36364d7808d928 [file] [log] [blame]
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "PageLoadState.h"
namespace WebKit {
// Progress always starts at this value. This helps provide feedback as soon as a load starts.
static const double initialProgressValue = 0.1;
PageLoadState::PageLoadState()
: m_mayHaveUncommittedChanges(false)
, m_outstandingTransactionCount(0)
{
}
PageLoadState::~PageLoadState()
{
ASSERT(m_observers.isEmpty());
}
void PageLoadState::addObserver(Observer& observer)
{
ASSERT(!m_observers.contains(&observer));
m_observers.append(&observer);
}
void PageLoadState::removeObserver(Observer& observer)
{
ASSERT(m_observers.contains(&observer));
size_t index = m_observers.find(&observer);
m_observers.remove(index);
}
void PageLoadState::endTransaction()
{
ASSERT(m_outstandingTransactionCount > 0);
if (!--m_outstandingTransactionCount)
commitChanges();
}
void PageLoadState::commitChanges()
{
if (!m_mayHaveUncommittedChanges)
return;
m_mayHaveUncommittedChanges = false;
bool titleChanged = m_committedState.title != m_uncommittedState.title;
bool isLoadingChanged = isLoadingState(m_committedState.state) != isLoadingState(m_uncommittedState.state);
bool activeURLChanged = activeURL(m_committedState) != activeURL(m_uncommittedState);
bool hasOnlySecureContentChanged = hasOnlySecureContent(m_committedState) != hasOnlySecureContent(m_uncommittedState);
bool estimatedProgressChanged = estimatedProgress(m_committedState) != estimatedProgress(m_uncommittedState);
if (titleChanged)
callObserverCallback(&Observer::willChangeTitle);
if (isLoadingChanged)
callObserverCallback(&Observer::willChangeIsLoading);
if (activeURLChanged)
callObserverCallback(&Observer::willChangeActiveURL);
if (hasOnlySecureContentChanged)
callObserverCallback(&Observer::willChangeHasOnlySecureContent);
if (estimatedProgressChanged)
callObserverCallback(&Observer::willChangeEstimatedProgress);
m_committedState = m_uncommittedState;
// The "did" ordering is the reverse of the "will". This is a requirement of Cocoa Key-Value Observing.
if (estimatedProgressChanged)
callObserverCallback(&Observer::didChangeEstimatedProgress);
if (hasOnlySecureContentChanged)
callObserverCallback(&Observer::didChangeHasOnlySecureContent);
if (activeURLChanged)
callObserverCallback(&Observer::didChangeActiveURL);
if (isLoadingChanged)
callObserverCallback(&Observer::didChangeIsLoading);
if (titleChanged)
callObserverCallback(&Observer::didChangeTitle);
}
void PageLoadState::reset(const Transaction::Token& token)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
m_uncommittedState.state = State::Finished;
m_uncommittedState.hasInsecureContent = false;
m_uncommittedState.pendingAPIRequestURL = String();
m_uncommittedState.provisionalURL = String();
m_uncommittedState.url = String();
m_uncommittedState.unreachableURL = String();
m_lastUnreachableURL = String();
m_uncommittedState.title = String();
m_uncommittedState.estimatedProgress = 0;
}
bool PageLoadState::isLoading() const
{
return isLoadingState(m_committedState.state);
}
String PageLoadState::activeURL(const Data& data)
{
// If there is a currently pending URL, it is the active URL,
// even when there's no main frame yet, as it might be the
// first API request.
if (!data.pendingAPIRequestURL.isNull())
return data.pendingAPIRequestURL;
if (!data.unreachableURL.isEmpty())
return data.unreachableURL;
switch (data.state) {
case State::Provisional:
return data.provisionalURL;
case State::Committed:
case State::Finished:
return data.url;
}
ASSERT_NOT_REACHED();
return String();
}
String PageLoadState::activeURL() const
{
return activeURL(m_committedState);
}
bool PageLoadState::hasOnlySecureContent(const Data& data)
{
if (data.hasInsecureContent)
return false;
return data.url.startsWith("https:", false);
}
bool PageLoadState::hasOnlySecureContent() const
{
return hasOnlySecureContent(m_committedState);
}
double PageLoadState::estimatedProgress(const Data& data)
{
if (!data.pendingAPIRequestURL.isNull())
return initialProgressValue;
return data.estimatedProgress;
}
double PageLoadState::estimatedProgress() const
{
return estimatedProgress(m_committedState);
}
const String& PageLoadState::pendingAPIRequestURL() const
{
return m_committedState.pendingAPIRequestURL;
}
void PageLoadState::setPendingAPIRequestURL(const Transaction::Token& token, const String& pendingAPIRequestURL)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
m_uncommittedState.pendingAPIRequestURL = pendingAPIRequestURL;
}
void PageLoadState::clearPendingAPIRequestURL(const Transaction::Token& token)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
m_uncommittedState.pendingAPIRequestURL = String();
}
void PageLoadState::didStartProvisionalLoad(const Transaction::Token& token, const String& url, const String& unreachableURL)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
ASSERT(m_uncommittedState.provisionalURL.isEmpty());
m_uncommittedState.state = State::Provisional;
m_uncommittedState.provisionalURL = url;
setUnreachableURL(token, unreachableURL);
}
void PageLoadState::didReceiveServerRedirectForProvisionalLoad(const Transaction::Token& token, const String& url)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
ASSERT(m_uncommittedState.state == State::Provisional);
m_uncommittedState.provisionalURL = url;
}
void PageLoadState::didFailProvisionalLoad(const Transaction::Token& token)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
ASSERT(m_uncommittedState.state == State::Provisional);
m_uncommittedState.state = State::Finished;
m_uncommittedState.provisionalURL = String();
m_uncommittedState.unreachableURL = m_lastUnreachableURL;
}
void PageLoadState::didCommitLoad(const Transaction::Token& token)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
ASSERT(m_uncommittedState.state == State::Provisional);
m_uncommittedState.state = State::Committed;
m_uncommittedState.hasInsecureContent = false;
m_uncommittedState.url = m_uncommittedState.provisionalURL;
m_uncommittedState.provisionalURL = String();
m_uncommittedState.title = String();
}
void PageLoadState::didFinishLoad(const Transaction::Token& token)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
ASSERT(m_uncommittedState.state == State::Committed);
ASSERT(m_uncommittedState.provisionalURL.isEmpty());
m_uncommittedState.state = State::Finished;
}
void PageLoadState::didFailLoad(const Transaction::Token& token)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
ASSERT(m_uncommittedState.provisionalURL.isEmpty());
m_uncommittedState.state = State::Finished;
}
void PageLoadState::didSameDocumentNavigation(const Transaction::Token& token, const String& url)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
ASSERT(!m_uncommittedState.url.isEmpty());
m_uncommittedState.url = url;
}
void PageLoadState::didDisplayOrRunInsecureContent(const Transaction::Token& token)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
m_uncommittedState.hasInsecureContent = true;
}
void PageLoadState::setUnreachableURL(const Transaction::Token& token, const String& unreachableURL)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
m_lastUnreachableURL = m_uncommittedState.unreachableURL;
m_uncommittedState.unreachableURL = unreachableURL;
}
const String& PageLoadState::title() const
{
return m_committedState.title;
}
void PageLoadState::setTitle(const Transaction::Token& token, const String& title)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
m_uncommittedState.title = title;
}
void PageLoadState::didStartProgress(const Transaction::Token& token)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
m_uncommittedState.estimatedProgress = initialProgressValue;
}
void PageLoadState::didChangeProgress(const Transaction::Token& token, double value)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
m_uncommittedState.estimatedProgress = value;
}
void PageLoadState::didFinishProgress(const Transaction::Token& token)
{
ASSERT_UNUSED(token, &token.m_pageLoadState == this);
m_uncommittedState.estimatedProgress = 1;
}
bool PageLoadState::isLoadingState(State state)
{
switch (state) {
case State::Provisional:
case State::Committed:
return true;
case State::Finished:
return false;
}
ASSERT_NOT_REACHED();
return false;
}
void PageLoadState::callObserverCallback(void (Observer::*callback)())
{
for (auto* observer : m_observers)
(observer->*callback)();
}
} // namespace WebKit