diff options
Diffstat (limited to 'Source/WebCore/page/History.cpp')
-rw-r--r-- | Source/WebCore/page/History.cpp | 123 |
1 files changed, 99 insertions, 24 deletions
diff --git a/Source/WebCore/page/History.cpp b/Source/WebCore/page/History.cpp index 79bf90bfe..b9571c1d4 100644 --- a/Source/WebCore/page/History.cpp +++ b/Source/WebCore/page/History.cpp @@ -10,10 +10,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * 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 @@ -34,16 +34,19 @@ #include "FrameLoaderClient.h" #include "HistoryController.h" #include "HistoryItem.h" +#include "MainFrame.h" #include "Page.h" +#include "ScriptController.h" #include "SecurityOrigin.h" #include "SerializedScriptValue.h" +#include <wtf/CheckedArithmetic.h> #include <wtf/MainThread.h> namespace WebCore { History::History(Frame* frame) : DOMWindowProperty(frame) - , m_lastStateObjectRequested(0) + , m_lastStateObjectRequested(nullptr) { } @@ -53,7 +56,7 @@ unsigned History::length() const return 0; if (!m_frame->page()) return 0; - return m_frame->page()->backForward()->count(); + return m_frame->page()->backForward().count(); } PassRefPtr<SerializedScriptValue> History::state() @@ -67,7 +70,7 @@ PassRefPtr<SerializedScriptValue> History::stateInternal() const if (!m_frame) return 0; - if (HistoryItem* historyItem = m_frame->loader()->history()->currentItem()) + if (HistoryItem* historyItem = m_frame->loader().history().currentItem()) return historyItem->stateObject(); return 0; @@ -108,7 +111,7 @@ void History::go(int distance) if (!m_frame) return; - m_frame->navigationScheduler()->scheduleHistoryNavigation(distance); + m_frame->navigationScheduler().scheduleHistoryNavigation(distance); } void History::go(ScriptExecutionContext* context, int distance) @@ -117,48 +120,120 @@ void History::go(ScriptExecutionContext* context, int distance) return; ASSERT(isMainThread()); - Document* activeDocument = toDocument(context); + Document* activeDocument = downcast<Document>(context); if (!activeDocument) return; if (!activeDocument->canNavigate(m_frame)) return; - m_frame->navigationScheduler()->scheduleHistoryNavigation(distance); + m_frame->navigationScheduler().scheduleHistoryNavigation(distance); } -KURL History::urlForState(const String& urlString) +URL History::urlForState(const String& urlString) { - KURL baseURL = m_frame->document()->baseURL(); + URL baseURL = m_frame->document()->baseURL(); if (urlString.isEmpty()) return baseURL; - return KURL(baseURL, urlString); + return URL(baseURL, urlString); } -void History::stateObjectAdded(PassRefPtr<SerializedScriptValue> data, const String& title, const String& urlString, StateObjectType stateObjectType, ExceptionCode& ec) +void History::stateObjectAdded(PassRefPtr<SerializedScriptValue> data, const String& title, const String& urlString, StateObjectType stateObjectType, ExceptionCodeWithMessage& ec) { + // Each unique main-frame document is only allowed to send 64mb of state object payload to the UI client/process. + static uint32_t totalStateObjectPayloadLimit = 0x4000000; + static unsigned perUserGestureStateObjectLimit = 100; + if (!m_frame || !m_frame->page()) return; - - KURL fullURL = urlForState(urlString); + + URL fullURL = urlForState(urlString); if (!fullURL.isValid() || !m_frame->document()->securityOrigin()->canRequest(fullURL)) { - ec = SECURITY_ERR; + ec.code = SECURITY_ERR; + return; + } + + Document* mainDocument = m_frame->page()->mainFrame().document(); + History* mainHistory = nullptr; + if (mainDocument) { + if (auto* mainDOMWindow = mainDocument->domWindow()) + mainHistory = mainDOMWindow->history(); + } + + if (!mainHistory) + return; + + bool processingUserGesture = ScriptController::processingUserGesture(); + if (!processingUserGesture && mainHistory->m_nonUserGestureObjectsAdded >= perUserGestureStateObjectLimit) { + ec.code = SECURITY_ERR; + if (stateObjectType == StateObjectType::Replace) + ec.message = String::format("Attempt to use history.replaceState() more than %u times without a user gesture", perUserGestureStateObjectLimit); + else + ec.message = String::format("Attempt to use history.pushState() more than %u times without a user gesture", perUserGestureStateObjectLimit); + return; + } + + double userGestureTimestamp = mainDocument->lastHandledUserGestureTimestamp(); + if (processingUserGesture) { + if (mainHistory->m_currentUserGestureTimestamp < userGestureTimestamp) { + mainHistory->m_currentUserGestureTimestamp = userGestureTimestamp; + mainHistory->m_currentUserGestureObjectsAdded = 0; + } + + if (mainHistory->m_currentUserGestureObjectsAdded >= perUserGestureStateObjectLimit) { + ec.code = SECURITY_ERR; + if (stateObjectType == StateObjectType::Replace) + ec.message = String::format("Attempt to use history.replaceState() more than %u times per gesture", perUserGestureStateObjectLimit); + else + ec.message = String::format("Attempt to use history.pushState() more than %u times per user gesture", perUserGestureStateObjectLimit); + return; + } + } + + Checked<unsigned> titleSize = title.length(); + titleSize *= 2; + + Checked<unsigned> urlSize = fullURL.string().length(); + urlSize *= 2; + + Checked<uint64_t> payloadSize = titleSize; + payloadSize += urlSize; + payloadSize += data ? data->data().size() : 0; + + Checked<uint64_t> newTotalUsage = mainHistory->m_totalStateObjectUsage; + + if (stateObjectType == StateObjectType::Replace) + newTotalUsage -= m_mostRecentStateObjectUsage; + newTotalUsage += payloadSize; + + if (newTotalUsage > totalStateObjectPayloadLimit) { + ec.code = QUOTA_EXCEEDED_ERR; + if (stateObjectType == StateObjectType::Replace) + ec.message = ASCIILiteral("Attempt to store more data than allowed using history.replaceState()"); + else + ec.message = ASCIILiteral("Attempt to store more data than allowed using history.pushState()"); return; } - if (stateObjectType == StateObjectPush) - m_frame->loader()->history()->pushState(data, title, fullURL.string()); - else if (stateObjectType == StateObjectReplace) - m_frame->loader()->history()->replaceState(data, title, fullURL.string()); - + m_mostRecentStateObjectUsage = payloadSize.unsafeGet(); + + mainHistory->m_totalStateObjectUsage = newTotalUsage.unsafeGet(); + if (processingUserGesture) + ++mainHistory->m_currentUserGestureObjectsAdded; + else + ++mainHistory->m_nonUserGestureObjectsAdded; + if (!urlString.isEmpty()) m_frame->document()->updateURLForPushOrReplaceState(fullURL); - if (stateObjectType == StateObjectPush) - m_frame->loader()->client()->dispatchDidPushStateWithinPage(); - else if (stateObjectType == StateObjectReplace) - m_frame->loader()->client()->dispatchDidReplaceStateWithinPage(); + if (stateObjectType == StateObjectType::Push) { + m_frame->loader().history().pushState(data, title, fullURL.string()); + m_frame->loader().client().dispatchDidPushStateWithinPage(); + } else if (stateObjectType == StateObjectType::Replace) { + m_frame->loader().history().replaceState(data, title, fullURL.string()); + m_frame->loader().client().dispatchDidReplaceStateWithinPage(); + } } } // namespace WebCore |