RedirectScheduler.cpp [plain text]
#include "config.h"
#include "RedirectScheduler.h"
#include "BackForwardList.h"
#include "DocumentLoader.h"
#include "Event.h"
#include "FormState.h"
#include "Frame.h"
#include "FrameLoadRequest.h"
#include "FrameLoader.h"
#include "HistoryItem.h"
#include "HTMLFormElement.h"
#include "HTMLFrameOwnerElement.h"
#include "Page.h"
#include <wtf/CurrentTime.h>
namespace WebCore {
struct ScheduledRedirection : Noncopyable {
enum Type { redirection, locationChange, historyNavigation, formSubmission };
const Type type;
const double delay;
const String url;
const String referrer;
const FrameLoadRequest frameRequest;
const RefPtr<Event> event;
const RefPtr<FormState> formState;
const int historySteps;
const bool lockHistory;
const bool lockBackForwardList;
const bool wasUserGesture;
const bool wasRefresh;
const bool wasDuringLoad;
bool toldClient;
ScheduledRedirection(double delay, const String& url, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh)
: type(redirection)
, delay(delay)
, url(url)
, historySteps(0)
, lockHistory(lockHistory)
, lockBackForwardList(lockBackForwardList)
, wasUserGesture(wasUserGesture)
, wasRefresh(refresh)
, wasDuringLoad(false)
, toldClient(false)
{
ASSERT(!url.isEmpty());
}
ScheduledRedirection(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh, bool duringLoad)
: type(locationChange)
, delay(0)
, url(url)
, referrer(referrer)
, historySteps(0)
, lockHistory(lockHistory)
, lockBackForwardList(lockBackForwardList)
, wasUserGesture(wasUserGesture)
, wasRefresh(refresh)
, wasDuringLoad(duringLoad)
, toldClient(false)
{
ASSERT(!url.isEmpty());
}
explicit ScheduledRedirection(int historyNavigationSteps)
: type(historyNavigation)
, delay(0)
, historySteps(historyNavigationSteps)
, lockHistory(false)
, lockBackForwardList(false)
, wasUserGesture(false)
, wasRefresh(false)
, wasDuringLoad(false)
, toldClient(false)
{
}
ScheduledRedirection(const FrameLoadRequest& frameRequest,
bool lockHistory, bool lockBackForwardList, PassRefPtr<Event> event, PassRefPtr<FormState> formState,
bool duringLoad)
: type(formSubmission)
, delay(0)
, frameRequest(frameRequest)
, event(event)
, formState(formState)
, historySteps(0)
, lockHistory(lockHistory)
, lockBackForwardList(lockBackForwardList)
, wasUserGesture(false)
, wasRefresh(false)
, wasDuringLoad(duringLoad)
, toldClient(false)
{
ASSERT(!frameRequest.isEmpty());
ASSERT(this->formState);
}
};
RedirectScheduler::RedirectScheduler(Frame* frame)
: m_frame(frame)
, m_timer(this, &RedirectScheduler::timerFired)
{
}
RedirectScheduler::~RedirectScheduler()
{
}
bool RedirectScheduler::redirectScheduledDuringLoad()
{
return m_scheduledRedirection && m_scheduledRedirection->wasDuringLoad;
}
void RedirectScheduler::clear()
{
m_timer.stop();
m_scheduledRedirection.clear();
}
void RedirectScheduler::scheduleRedirect(double delay, const String& url)
{
if (delay < 0 || delay > INT_MAX / 1000)
return;
if (!m_frame->page())
return;
if (url.isEmpty())
return;
if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay)
schedule(new ScheduledRedirection(delay, url, true, delay <= 1, false, false));
}
bool RedirectScheduler::mustLockBackForwardList(Frame* targetFrame)
{
for (Frame* ancestor = targetFrame->tree()->parent(); ancestor; ancestor = ancestor->tree()->parent()) {
Document* document = ancestor->document();
if (!ancestor->loader()->isComplete() || (document && document->processingLoadEvent()))
return true;
}
return false;
}
void RedirectScheduler::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture)
{
if (!m_frame->page())
return;
if (url.isEmpty())
return;
lockBackForwardList = lockBackForwardList || mustLockBackForwardList(m_frame);
FrameLoader* loader = m_frame->loader();
KURL parsedURL(ParsedURLString, url);
if (parsedURL.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(loader->url(), parsedURL)) {
loader->changeLocation(loader->completeURL(url), referrer, lockHistory, lockBackForwardList, wasUserGesture);
return;
}
bool duringLoad = !loader->committedFirstRealDocumentLoad();
schedule(new ScheduledRedirection(url, referrer, lockHistory, lockBackForwardList, wasUserGesture, false, duringLoad));
}
void RedirectScheduler::scheduleFormSubmission(const FrameLoadRequest& frameRequest,
bool lockHistory, PassRefPtr<Event> event, PassRefPtr<FormState> formState)
{
ASSERT(m_frame->page());
ASSERT(!frameRequest.isEmpty());
bool duringLoad = !m_frame->loader()->committedFirstRealDocumentLoad();
bool lockBackForwardList = mustLockBackForwardList(m_frame) || (formState->formSubmissionTrigger() == SubmittedByJavaScript && m_frame->tree()->parent());
schedule(new ScheduledRedirection(frameRequest, lockHistory, lockBackForwardList, event, formState, duringLoad));
}
void RedirectScheduler::scheduleRefresh(bool wasUserGesture)
{
if (!m_frame->page())
return;
const KURL& url = m_frame->loader()->url();
if (url.isEmpty())
return;
schedule(new ScheduledRedirection(url.string(), m_frame->loader()->outgoingReferrer(), true, true, wasUserGesture, true, false));
}
bool RedirectScheduler::locationChangePending()
{
if (!m_scheduledRedirection)
return false;
switch (m_scheduledRedirection->type) {
case ScheduledRedirection::redirection:
return false;
case ScheduledRedirection::historyNavigation:
case ScheduledRedirection::locationChange:
case ScheduledRedirection::formSubmission:
return true;
}
ASSERT_NOT_REACHED();
return false;
}
void RedirectScheduler::scheduleHistoryNavigation(int steps)
{
if (!m_frame->page())
return;
HistoryItem* specifiedEntry = m_frame->page()->backForwardList()->itemAtIndex(steps);
if (!specifiedEntry) {
cancel();
return;
}
#if !ENABLE(HISTORY_ALWAYS_ASYNC)
HistoryItem* currentEntry = m_frame->loader()->history()->currentItem();
if (currentEntry != specifiedEntry && currentEntry->documentSequenceNumber() == specifiedEntry->documentSequenceNumber()) {
m_frame->loader()->history()->goToItem(specifiedEntry, FrameLoadTypeIndexedBackForward);
return;
}
#endif
schedule(new ScheduledRedirection(steps));
}
void RedirectScheduler::timerFired(Timer<RedirectScheduler>*)
{
if (!m_frame->page())
return;
if (m_frame->page()->defersLoading())
return;
OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release());
FrameLoader* loader = m_frame->loader();
switch (redirection->type) {
case ScheduledRedirection::redirection:
case ScheduledRedirection::locationChange:
loader->changeLocation(KURL(ParsedURLString, redirection->url), redirection->referrer,
redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture, redirection->wasRefresh);
return;
case ScheduledRedirection::historyNavigation:
if (redirection->historySteps == 0) {
loader->urlSelected(loader->url(), "", 0, redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture, SendReferrer);
return;
}
m_frame->page()->goBackOrForward(redirection->historySteps);
return;
case ScheduledRedirection::formSubmission:
if (!redirection->formState->sourceFrame()->loader()->shouldAllowNavigation(m_frame))
return;
loader->loadFrameRequest(redirection->frameRequest, redirection->lockHistory, redirection->lockBackForwardList,
redirection->event, redirection->formState, SendReferrer);
return;
}
ASSERT_NOT_REACHED();
}
void RedirectScheduler::schedule(PassOwnPtr<ScheduledRedirection> redirection)
{
ASSERT(m_frame->page());
FrameLoader* loader = m_frame->loader();
if (redirection->wasDuringLoad) {
if (DocumentLoader* provisionalDocumentLoader = loader->provisionalDocumentLoader())
provisionalDocumentLoader->stopLoading();
loader->stopLoading(UnloadEventPolicyUnloadAndPageHide);
}
cancel();
m_scheduledRedirection = redirection;
if (!loader->isComplete() && m_scheduledRedirection->type != ScheduledRedirection::redirection)
loader->completed();
startTimer();
}
void RedirectScheduler::startTimer()
{
if (!m_scheduledRedirection)
return;
ASSERT(m_frame->page());
FrameLoader* loader = m_frame->loader();
if (m_timer.isActive())
return;
if (m_scheduledRedirection->type == ScheduledRedirection::redirection && !loader->allAncestorsAreComplete())
return;
m_timer.startOneShot(m_scheduledRedirection->delay);
switch (m_scheduledRedirection->type) {
case ScheduledRedirection::locationChange:
case ScheduledRedirection::redirection:
if (m_scheduledRedirection->toldClient)
return;
m_scheduledRedirection->toldClient = true;
loader->clientRedirected(KURL(ParsedURLString, m_scheduledRedirection->url),
m_scheduledRedirection->delay,
currentTime() + m_timer.nextFireInterval(),
m_scheduledRedirection->lockBackForwardList);
return;
case ScheduledRedirection::formSubmission:
return;
case ScheduledRedirection::historyNavigation:
return;
}
ASSERT_NOT_REACHED();
}
void RedirectScheduler::cancel(bool newLoadInProgress)
{
m_timer.stop();
OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release());
if (redirection && redirection->toldClient)
m_frame->loader()->clientRedirectCancelledOrFinished(newLoadInProgress);
}
}