diff options
-rw-r--r-- | src/core/web_contents_adapter.cpp | 172 | ||||
-rw-r--r-- | src/core/web_contents_adapter.h | 2 | ||||
-rw-r--r-- | src/webenginewidgets/api/qwebenginehistory.cpp | 6 | ||||
-rw-r--r-- | src/webenginewidgets/api/qwebenginepage.cpp | 9 | ||||
-rw-r--r-- | src/webenginewidgets/api/qwebenginepage_p.h | 1 | ||||
-rw-r--r-- | tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp | 4 |
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, ¤tIndex, &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); |