summaryrefslogtreecommitdiff
path: root/Source/WebCore/page/History.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/page/History.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/page/History.cpp')
-rw-r--r--Source/WebCore/page/History.cpp141
1 files changed, 101 insertions, 40 deletions
diff --git a/Source/WebCore/page/History.cpp b/Source/WebCore/page/History.cpp
index 5b5539cb5..fbc52f3c9 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,18 @@
#include "FrameLoaderClient.h"
#include "HistoryController.h"
#include "HistoryItem.h"
+#include "Logging.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)
+History::History(Frame& frame)
+ : DOMWindowProperty(&frame)
{
}
@@ -51,26 +53,26 @@ unsigned History::length() const
{
if (!m_frame)
return 0;
- if (!m_frame->page())
+ auto* page = m_frame->page();
+ if (!page)
return 0;
- return m_frame->page()->backForward().count();
+ return page->backForward().count();
}
-PassRefPtr<SerializedScriptValue> History::state()
+SerializedScriptValue* History::state()
{
m_lastStateObjectRequested = stateInternal();
- return m_lastStateObjectRequested;
+ return m_lastStateObjectRequested.get();
}
-PassRefPtr<SerializedScriptValue> History::stateInternal() const
+SerializedScriptValue* History::stateInternal() const
{
if (!m_frame)
- return 0;
-
- if (HistoryItem* historyItem = m_frame->loader().history().currentItem())
- return historyItem->stateObject();
-
- return 0;
+ return nullptr;
+ auto* historyItem = m_frame->loader().history().currentItem();
+ if (!historyItem)
+ return nullptr;
+ return historyItem->stateObject();
}
bool History::stateChanged() const
@@ -80,7 +82,7 @@ bool History::stateChanged() const
bool History::isSameAsCurrentState(SerializedScriptValue* state) const
{
- return state == stateInternal().get();
+ return state == stateInternal();
}
void History::back()
@@ -88,9 +90,9 @@ void History::back()
go(-1);
}
-void History::back(ScriptExecutionContext* context)
+void History::back(Document& document)
{
- go(context, -1);
+ go(document, -1);
}
void History::forward()
@@ -98,30 +100,31 @@ void History::forward()
go(1);
}
-void History::forward(ScriptExecutionContext* context)
+void History::forward(Document& document)
{
- go(context, 1);
+ go(document, 1);
}
void History::go(int distance)
{
+ LOG(History, "History %p go(%d) frame %p (main frame %d)", this, distance, m_frame, m_frame ? m_frame->isMainFrame() : false);
+
if (!m_frame)
return;
m_frame->navigationScheduler().scheduleHistoryNavigation(distance);
}
-void History::go(ScriptExecutionContext* context, int distance)
+void History::go(Document& document, int distance)
{
+ LOG(History, "History %p go(%d) in document %p frame %p (main frame %d)", this, distance, &document, m_frame, m_frame ? m_frame->isMainFrame() : false);
+
if (!m_frame)
return;
ASSERT(isMainThread());
- Document* activeDocument = toDocument(context);
- if (!activeDocument)
- return;
- if (!activeDocument->canNavigate(m_frame))
+ if (!document.canNavigate(m_frame))
return;
m_frame->navigationScheduler().scheduleHistoryNavigation(distance);
@@ -136,29 +139,87 @@ URL History::urlForState(const String& urlString)
return URL(baseURL, urlString);
}
-void History::stateObjectAdded(PassRefPtr<SerializedScriptValue> data, const String& title, const String& urlString, StateObjectType stateObjectType, ExceptionCode& ec)
+ExceptionOr<void> History::stateObjectAdded(RefPtr<SerializedScriptValue>&& data, const String& title, const String& urlString, StateObjectType stateObjectType)
{
+ // 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 double stateObjectTimeSpan = 30.0;
+ static unsigned perStateObjectTimeSpanLimit = 100;
+
if (!m_frame || !m_frame->page())
- return;
-
+ return { };
+
URL fullURL = urlForState(urlString);
- if (!fullURL.isValid() || !m_frame->document()->securityOrigin()->canRequest(fullURL)) {
- ec = SECURITY_ERR;
- return;
+ if (!fullURL.isValid() || !m_frame->document()->securityOrigin().canRequest(fullURL))
+ return Exception { SECURITY_ERR };
+
+ if (fullURL.hasUsername() || fullURL.hasPassword()) {
+ if (stateObjectType == StateObjectType::Replace)
+ return Exception { SECURITY_ERR, "Attempt to use history.replaceState() to change session history URL to " + fullURL.string() + " is insecure; Username/passwords aren't allowed in state object URLs" };
+ return Exception { SECURITY_ERR, "Attempt to use history.pushState() to add URL " + fullURL.string() + " to session history is insecure; Username/passwords aren't allowed in state object URLs" };
+ }
+
+ Document* mainDocument = m_frame->page()->mainFrame().document();
+ History* mainHistory = nullptr;
+ if (mainDocument) {
+ if (auto* mainDOMWindow = mainDocument->domWindow())
+ mainHistory = mainDOMWindow->history();
+ }
+
+ if (!mainHistory)
+ return { };
+
+ double currentTimestamp = currentTime();
+ if (currentTimestamp - mainHistory->m_currentStateObjectTimeSpanStart > stateObjectTimeSpan) {
+ mainHistory->m_currentStateObjectTimeSpanStart = currentTimestamp;
+ mainHistory->m_currentStateObjectTimeSpanObjectsAdded = 0;
+ }
+
+ if (mainHistory->m_currentStateObjectTimeSpanObjectsAdded >= perStateObjectTimeSpanLimit) {
+ if (stateObjectType == StateObjectType::Replace)
+ return Exception { SECURITY_ERR, String::format("Attempt to use history.replaceState() more than %u times per %f seconds", perStateObjectTimeSpanLimit, stateObjectTimeSpan) };
+ return Exception { SECURITY_ERR, String::format("Attempt to use history.pushState() more than %u times per %f seconds", perStateObjectTimeSpanLimit, stateObjectTimeSpan) };
}
- if (stateObjectType == StateObjectType::Push)
- m_frame->loader().history().pushState(data, title, fullURL.string());
- else if (stateObjectType == StateObjectType::Replace)
- m_frame->loader().history().replaceState(data, title, fullURL.string());
-
+ 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) {
+ if (stateObjectType == StateObjectType::Replace)
+ return Exception { QUOTA_EXCEEDED_ERR, ASCIILiteral("Attempt to store more data than allowed using history.replaceState()") };
+ return Exception { QUOTA_EXCEEDED_ERR, ASCIILiteral("Attempt to store more data than allowed using history.pushState()") };
+ }
+
+ m_mostRecentStateObjectUsage = payloadSize.unsafeGet();
+
+ mainHistory->m_totalStateObjectUsage = newTotalUsage.unsafeGet();
+ ++mainHistory->m_currentStateObjectTimeSpanObjectsAdded;
+
if (!urlString.isEmpty())
m_frame->document()->updateURLForPushOrReplaceState(fullURL);
- if (stateObjectType == StateObjectType::Push)
+ if (stateObjectType == StateObjectType::Push) {
+ m_frame->loader().history().pushState(WTFMove(data), title, fullURL.string());
m_frame->loader().client().dispatchDidPushStateWithinPage();
- else if (stateObjectType == StateObjectType::Replace)
+ } else if (stateObjectType == StateObjectType::Replace) {
+ m_frame->loader().history().replaceState(WTFMove(data), title, fullURL.string());
m_frame->loader().client().dispatchDidReplaceStateWithinPage();
+ }
+
+ return { };
}
} // namespace WebCore