summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/web_contents_adapter.cpp172
-rw-r--r--src/core/web_contents_adapter.h2
-rw-r--r--src/webenginewidgets/api/qwebenginehistory.cpp6
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp9
-rw-r--r--src/webenginewidgets/api/qwebenginepage_p.h1
-rw-r--r--tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp4
6 files changed, 182 insertions, 12 deletions
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 465c7be9d..7dad03e45 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -38,6 +38,11 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
#include "web_contents_adapter.h"
#include "browser_context_qt.h"
@@ -53,8 +58,10 @@
#include "base/values.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_view_host.h"
+#include "content/public/common/page_state.h"
#include "content/public/common/page_zoom.h"
#include "content/public/common/renderer_preferences.h"
#include "content/public/common/url_constants.h"
@@ -69,6 +76,7 @@
static const int kTestWindowWidth = 800;
static const int kTestWindowHeight = 600;
+static const int kHistoryStreamVersion = 3;
static QVariant fromJSValue(const base::Value *result)
{
@@ -165,6 +173,123 @@ static QStringList listRecursively(const QDir& dir) {
return ret;
}
+static content::WebContents *createBlankWebContents(WebContentsAdapterClient *adapterClient)
+{
+ content::BrowserContext* browserContext = ContentBrowserClientQt::Get()->browser_context();
+ content::WebContents::CreateParams create_params(browserContext, NULL);
+ create_params.routing_id = MSG_ROUTING_NONE;
+ create_params.initial_size = gfx::Size(kTestWindowWidth, kTestWindowHeight);
+ create_params.context = reinterpret_cast<gfx::NativeView>(adapterClient);
+ return content::WebContents::Create(create_params);
+}
+
+static void serializeNavigationHistory(const content::NavigationController &controller, QDataStream &output)
+{
+ const int currentIndex = controller.GetCurrentEntryIndex();
+ const int count = controller.GetEntryCount();
+ const int pendingIndex = controller.GetPendingEntryIndex();
+
+ output << kHistoryStreamVersion;
+ output << count;
+ output << currentIndex;
+
+ // Logic taken from SerializedNavigationEntry::WriteToPickle.
+ for (int i = 0; i < count; ++i) {
+ const content::NavigationEntry* entry = (i == pendingIndex)
+ ? controller.GetPendingEntry()
+ : controller.GetEntryAtIndex(i);
+ if (entry->GetVirtualURL().is_valid()) {
+ if (entry->GetHasPostData())
+ entry->GetPageState().RemovePasswordData();
+ std::string encodedPageState = entry->GetPageState().ToEncodedData();
+ output << toQt(entry->GetVirtualURL());
+ output << toQt(entry->GetTitle());
+ output << QByteArray(encodedPageState.data(), encodedPageState.size());
+ output << static_cast<qint32>(entry->GetTransitionType());
+ output << entry->GetHasPostData();
+ output << toQt(entry->GetReferrer().url);
+ output << static_cast<qint32>(entry->GetReferrer().policy);
+ output << toQt(entry->GetOriginalRequestURL());
+ output << entry->GetIsOverridingUserAgent();
+ output << static_cast<qint64>(entry->GetTimestamp().ToInternalValue());
+ output << entry->GetHttpStatusCode();
+ // If you want to navigate a named frame in Chrome, you will first need to
+ // add support for persisting it. It is currently only used for layout tests.
+ CHECK(entry->GetFrameToNavigate().empty());
+ }
+ }
+}
+
+void deserializeNavigationHistory(QDataStream &input, int *currentIndex, std::vector<content::NavigationEntry*> *entries)
+{
+ int version;
+ input >> version;
+ if (version != kHistoryStreamVersion) {
+ // We do not try to decode previous history stream versions.
+ // Make sure that our history is cleared and mark the rest of the stream as invalid.
+ input.setStatus(QDataStream::ReadCorruptData);
+ *currentIndex = -1;
+ return;
+ }
+
+ int count;
+ input >> count >> *currentIndex;
+
+ int pageId = 0;
+ entries->reserve(count);
+ // Logic taken from SerializedNavigationEntry::ReadFromPickle and ToNavigationEntries.
+ for (int i = 0; i < count; ++i) {
+ QUrl virtualUrl, referrerUrl, originalRequestUrl;
+ QString title;
+ QByteArray pageState;
+ qint32 transitionType, referrerPolicy;
+ bool hasPostData, isOverridingUserAgent;
+ qint64 timestamp;
+ int httpStatusCode;
+ input >> virtualUrl;
+ input >> title;
+ input >> pageState;
+ input >> transitionType;
+ input >> hasPostData;
+ input >> referrerUrl;
+ input >> referrerPolicy;
+ input >> originalRequestUrl;
+ input >> isOverridingUserAgent;
+ input >> timestamp;
+ input >> httpStatusCode;
+
+ // If we couldn't unpack the entry successfully, abort everything.
+ if (input.status() != QDataStream::Ok) {
+ *currentIndex = -1;
+ Q_FOREACH (content::NavigationEntry *entry, *entries)
+ delete entry;
+ entries->clear();
+ return;
+ }
+
+ content::NavigationEntry *entry = content::NavigationController::CreateNavigationEntry(
+ toGurl(virtualUrl),
+ content::Referrer(toGurl(referrerUrl), static_cast<blink::WebReferrerPolicy>(referrerPolicy)),
+ // Use a transition type of reload so that we don't incorrectly
+ // increase the typed count.
+ content::PAGE_TRANSITION_RELOAD,
+ false,
+ // The extra headers are not sync'ed across sessions.
+ std::string(),
+ ContentBrowserClientQt::Get()->browser_context());
+
+ entry->SetTitle(toString16(title));
+ entry->SetPageState(content::PageState::CreateFromEncodedData(std::string(pageState.data(), pageState.size())));
+ entry->SetPageID(pageId++);
+ entry->SetHasPostData(hasPostData);
+ entry->SetOriginalRequestURL(toGurl(originalRequestUrl));
+ entry->SetIsOverridingUserAgent(isOverridingUserAgent);
+ entry->SetTimestamp(base::Time::FromInternalValue(timestamp));
+ entry->SetHttpStatusCode(httpStatusCode);
+ entries->push_back(entry);
+ }
+}
+
class WebContentsAdapterPrivate {
public:
WebContentsAdapterPrivate(WebContentsAdapterClient::RenderingMode renderingMode);
@@ -184,6 +309,34 @@ WebContentsAdapterPrivate::WebContentsAdapterPrivate(WebContentsAdapterClient::R
{
}
+QExplicitlySharedDataPointer<WebContentsAdapter> WebContentsAdapter::createFromSerializedNavigationHistory(QDataStream &input, WebContentsAdapterClient *adapterClient, WebContentsAdapterClient::RenderingMode renderingMode)
+{
+ int currentIndex;
+ std::vector<content::NavigationEntry*> entries;
+ deserializeNavigationHistory(input, &currentIndex, &entries);
+
+ if (currentIndex == -1)
+ return QExplicitlySharedDataPointer<WebContentsAdapter>();
+
+ // Unlike WebCore, Chromium only supports Restoring to a new WebContents instance.
+ content::WebContents* newWebContents = createBlankWebContents(adapterClient);
+ content::NavigationController &controller = newWebContents->GetController();
+ controller.Restore(currentIndex, content::NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, &entries);
+
+ if (controller.GetActiveEntry()) {
+ // Set up the file access rights for the selected navigation entry.
+ // TODO(joth): This is duplicated from chrome/.../session_restore.cc and
+ // should be shared e.g. in NavigationController. http://crbug.com/68222
+ const int id = newWebContents->GetRenderProcessHost()->GetID();
+ const content::PageState& pageState = controller.GetActiveEntry()->GetPageState();
+ const std::vector<base::FilePath>& filePaths = pageState.GetReferencedFiles();
+ for (std::vector<base::FilePath>::const_iterator file = filePaths.begin(); file != filePaths.end(); ++file)
+ content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(id, *file);
+ }
+
+ return QExplicitlySharedDataPointer<WebContentsAdapter>(new WebContentsAdapter(renderingMode, newWebContents));
+}
+
WebContentsAdapter::WebContentsAdapter(WebContentsAdapterClient::RenderingMode renderingMode, content::WebContents *webContents)
: d_ptr(new WebContentsAdapterPrivate(renderingMode))
{
@@ -201,14 +354,8 @@ void WebContentsAdapter::initialize(WebContentsAdapterClient *adapterClient)
d->adapterClient = adapterClient;
// Create our own if a WebContents wasn't provided at construction.
- if (!d->webContents) {
- content::BrowserContext* browserContext = ContentBrowserClientQt::Get()->browser_context();
- content::WebContents::CreateParams create_params(browserContext, NULL);
- create_params.routing_id = MSG_ROUTING_NONE;
- create_params.initial_size = gfx::Size(kTestWindowWidth, kTestWindowHeight);
- create_params.context = reinterpret_cast<gfx::NativeView>(adapterClient);
- d->webContents.reset(content::WebContents::Create(create_params));
- }
+ if (!d->webContents)
+ d->webContents.reset(createBlankWebContents(adapterClient));
content::RendererPreferences* rendererPrefs = d->webContents->GetMutableRendererPrefs();
rendererPrefs->use_custom_colors = true;
@@ -225,6 +372,9 @@ void WebContentsAdapter::initialize(WebContentsAdapterClient *adapterClient)
WebContentsViewQt* contentsView = static_cast<WebContentsViewQt*>(d->webContents->GetView());
contentsView->initialize(adapterClient);
+ // This should only be necessary after having restored the history to a new WebContentsAdapter.
+ d->webContents->GetController().LoadIfNecessary();
+
// Create a RenderView with the initial empty document
content::RenderViewHost *rvh = d->webContents->GetRenderViewHost();
Q_ASSERT(rvh);
@@ -422,6 +572,12 @@ void WebContentsAdapter::clearNavigationHistory()
d->webContents->GetController().PruneAllButLastCommitted();
}
+void WebContentsAdapter::serializeNavigationHistory(QDataStream &output)
+{
+ Q_D(WebContentsAdapter);
+ ::serializeNavigationHistory(d->webContents->GetController(), output);
+}
+
void WebContentsAdapter::setZoomFactor(qreal factor)
{
Q_D(WebContentsAdapter);
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index 3e5a0e24b..ba59252bf 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -56,6 +56,7 @@ class WebContentsAdapterPrivate;
class QWEBENGINE_EXPORT WebContentsAdapter : public QSharedData {
public:
+ static QExplicitlySharedDataPointer<WebContentsAdapter> createFromSerializedNavigationHistory(QDataStream &input, WebContentsAdapterClient *adapterClient, WebContentsAdapterClient::RenderingMode renderingMode);
// Takes ownership of the WebContents.
WebContentsAdapter(WebContentsAdapterClient::RenderingMode renderingMode, content::WebContents *webContents = 0);
~WebContentsAdapter();
@@ -89,6 +90,7 @@ public:
QUrl getNavigationEntryUrl(int index);
QString getNavigationEntryTitle(int index);
void clearNavigationHistory();
+ void serializeNavigationHistory(QDataStream &output);
void setZoomFactor(qreal);
qreal currentZoomFactor() const;
void enableInspector(bool);
diff --git a/src/webenginewidgets/api/qwebenginehistory.cpp b/src/webenginewidgets/api/qwebenginehistory.cpp
index d9e9b9c9f..7079bdfc7 100644
--- a/src/webenginewidgets/api/qwebenginehistory.cpp
+++ b/src/webenginewidgets/api/qwebenginehistory.cpp
@@ -281,15 +281,13 @@ void QWebEngineHistory::setMaximumItemCount(int count)
QDataStream& operator<<(QDataStream& stream, const QWebEngineHistory& history)
{
- Q_UNUSED(history);
- qWarning("Not implemented: %s", __func__);
+ history.d_func()->page->webContents()->serializeNavigationHistory(stream);
return stream;
}
QDataStream& operator>>(QDataStream& stream, QWebEngineHistory& history)
{
- Q_UNUSED(history);
- qWarning("Not implemented: %s", __func__);
+ history.d_func()->page->recreateFromSerializedHistory(stream);
return stream;
}
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index b6c84de6c..e9ad17cf0 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -354,6 +354,15 @@ void QWebEnginePagePrivate::_q_webActionTriggered(bool checked)
}
#endif // QT_NO_ACTION
+void QWebEnginePagePrivate::recreateFromSerializedHistory(QDataStream &input)
+{
+ QExplicitlySharedDataPointer<WebContentsAdapter> newWebContents = WebContentsAdapter::createFromSerializedNavigationHistory(input, this, WebContentsAdapterClient::SoftwareRenderingMode);
+ if (newWebContents) {
+ adapter = newWebContents.data();
+ adapter->initialize(this);
+ }
+}
+
QWebEnginePage::QWebEnginePage(QObject* parent)
: QObject(*new QWebEnginePagePrivate, parent)
{
diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h
index a69866cbd..927519008 100644
--- a/src/webenginewidgets/api/qwebenginepage_p.h
+++ b/src/webenginewidgets/api/qwebenginepage_p.h
@@ -142,6 +142,7 @@ public:
void _q_webActionTriggered(bool checked);
WebContentsAdapter *webContents() { return adapter.data(); }
+ void recreateFromSerializedHistory(QDataStream &input);
QExplicitlySharedDataPointer<WebContentsAdapter> adapter;
QWebEngineHistory *history;
diff --git a/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp
index f2b11c5c2..cebfee67a 100644
--- a/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp
+++ b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp
@@ -259,6 +259,7 @@ void tst_QWebEngineHistory::serialize_2()
// Force a "same document" navigation.
page->load(page->url().toString() + QLatin1String("#dummyAnchor"));
+ loadFinishedBarrier->ensureSignalEmitted();
int initialCurrentIndex = hist->currentItemIndex();
@@ -277,6 +278,8 @@ void tst_QWebEngineHistory::serialize_2()
QVERIFY(save.status() == QDataStream::Ok);
load >> *hist;
QVERIFY(load.status() == QDataStream::Ok);
+ // Restoring the history will trigger a load.
+ loadFinishedBarrier->ensureSignalEmitted();
//check current index
QCOMPARE(hist->currentItemIndex(), oldCurrentIndex);
@@ -562,6 +565,7 @@ void tst_QWebEngineHistory::restoreIncompatibleVersion1()
// This should fail to load, the history should be cleared and the stream should be broken.
stream >> *hist;
+ QEXPECT_FAIL("", "Behavior change: A broken stream won't clear the history in QtWebEngine.", Continue);
QVERIFY(!hist->canGoBack());
QVERIFY(!hist->canGoForward());
QVERIFY(stream.status() == QDataStream::ReadCorruptData);