summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-09-12 14:56:22 +0200
committerMichael BrĂ¼ning <michael.bruning@qt.io>2017-12-06 15:02:13 +0000
commit481155893fd85b4b0770397375ceaf520fcf9db6 (patch)
tree84727f5b93c31debe246995b0072ef1b818e3ef1
parent1c6cacf3020c0c201cd484ba165126123046e53b (diff)
Introduce devtools frontend
Makes it possible to use devtools without using the remote-debugger Task-number: QTBUG-47899 Task-number: QTBUG-50725 Task-number: QTBUG-50766 Change-Id: Id32e13f773372d9917599ebbb64ab4af61bbf1d8 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
-rw-r--r--examples/webengine/quicknanobrowser/BrowserWindow.qml16
-rw-r--r--examples/webenginewidgets/simplebrowser/browser.cpp14
-rw-r--r--examples/webenginewidgets/simplebrowser/browser.h1
-rw-r--r--examples/webenginewidgets/simplebrowser/browserwindow.cpp80
-rw-r--r--examples/webenginewidgets/simplebrowser/browserwindow.h3
-rw-r--r--examples/webenginewidgets/simplebrowser/tabwidget.cpp1
-rw-r--r--examples/webenginewidgets/simplebrowser/tabwidget.h1
-rw-r--r--examples/webenginewidgets/simplebrowser/webview.cpp8
-rw-r--r--examples/webenginewidgets/simplebrowser/webview.h1
m---------src/3rdparty0
-rw-r--r--src/core/core_chromium.pri2
-rw-r--r--src/core/devtools_frontend_qt.cpp519
-rw-r--r--src/core/devtools_frontend_qt.h139
-rw-r--r--src/core/render_widget_host_view_qt.cpp2
-rw-r--r--src/core/web_contents_adapter.cpp45
-rw-r--r--src/core/web_contents_adapter.h4
-rw-r--r--src/core/web_contents_adapter_p.h2
-rw-r--r--src/core/web_contents_delegate_qt.cpp5
-rw-r--r--src/core/web_contents_delegate_qt.h2
-rw-r--r--src/webengine/api/qquickwebengineview.cpp55
-rw-r--r--src/webengine/api/qquickwebengineview_p.h9
-rw-r--r--src/webengine/api/qquickwebengineview_p_p.h3
-rw-r--r--src/webengine/doc/src/webengineview_lgpl.qdoc22
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp89
-rw-r--r--src/webenginewidgets/api/qwebenginepage.h5
-rw-r--r--src/webenginewidgets/api/qwebenginepage_p.h4
26 files changed, 994 insertions, 38 deletions
diff --git a/examples/webengine/quicknanobrowser/BrowserWindow.qml b/examples/webengine/quicknanobrowser/BrowserWindow.qml
index 193f10ab3..f3bd8e457 100644
--- a/examples/webengine/quicknanobrowser/BrowserWindow.qml
+++ b/examples/webengine/quicknanobrowser/BrowserWindow.qml
@@ -88,6 +88,7 @@ ApplicationWindow {
property alias autoLoadIconsForPage: autoLoadIconsForPage.checked
property alias touchIconsEnabled: touchIconsEnabled.checked
property alias webRTCPublicInterfacesOnly : webRTCPublicInterfacesOnly.checked
+ property alias devToolsEnabled: devToolsEnabled.checked
}
Action {
@@ -318,6 +319,12 @@ ApplicationWindow {
checkable: true
checked: WebEngine.settings.webRTCPublicInterfacesOnly
}
+ MenuItem {
+ id: devToolsEnabled
+ text: "Open DevTools"
+ checkable: true
+ checked: false
+ }
}
}
}
@@ -476,6 +483,15 @@ ApplicationWindow {
}
}
}
+ WebEngineView {
+ id: devToolsView
+ visible: devToolsEnabled.checked
+ height: 400
+ inspectedView: tabs.currentIndex < tabs.count ? tabs.getTab(tabs.currentIndex).item : null
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ }
MessageDialog {
id: sslDialog
diff --git a/examples/webenginewidgets/simplebrowser/browser.cpp b/examples/webenginewidgets/simplebrowser/browser.cpp
index c50974531..5c6dbd35e 100644
--- a/examples/webenginewidgets/simplebrowser/browser.cpp
+++ b/examples/webenginewidgets/simplebrowser/browser.cpp
@@ -69,7 +69,19 @@ Browser::Browser()
BrowserWindow *Browser::createWindow(bool offTheRecord)
{
auto profile = offTheRecord ? &m_otrProfile : QWebEngineProfile::defaultProfile();
- auto mainWindow = new BrowserWindow(this, profile);
+ auto mainWindow = new BrowserWindow(this, profile, false);
+ m_windows.append(mainWindow);
+ QObject::connect(mainWindow, &QObject::destroyed, [this, mainWindow]() {
+ m_windows.removeOne(mainWindow);
+ });
+ mainWindow->show();
+ return mainWindow;
+}
+
+BrowserWindow *Browser::createDevToolsWindow()
+{
+ auto profile = QWebEngineProfile::defaultProfile();
+ auto mainWindow = new BrowserWindow(this, profile, true);
m_windows.append(mainWindow);
QObject::connect(mainWindow, &QObject::destroyed, [this, mainWindow]() {
m_windows.removeOne(mainWindow);
diff --git a/examples/webenginewidgets/simplebrowser/browser.h b/examples/webenginewidgets/simplebrowser/browser.h
index 9240cc987..fbc8465d2 100644
--- a/examples/webenginewidgets/simplebrowser/browser.h
+++ b/examples/webenginewidgets/simplebrowser/browser.h
@@ -66,6 +66,7 @@ public:
QVector<BrowserWindow*> windows() { return m_windows; }
BrowserWindow *createWindow(bool offTheRecord = false);
+ BrowserWindow *createDevToolsWindow();
DownloadManagerWidget &downloadManagerWidget() { return m_downloadManagerWidget; }
diff --git a/examples/webenginewidgets/simplebrowser/browserwindow.cpp b/examples/webenginewidgets/simplebrowser/browserwindow.cpp
index 016d58afe..c369a90fa 100644
--- a/examples/webenginewidgets/simplebrowser/browserwindow.cpp
+++ b/examples/webenginewidgets/simplebrowser/browserwindow.cpp
@@ -67,11 +67,11 @@
#include <QVBoxLayout>
#include <QWebEngineProfile>
-BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile)
+BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool forDevTools)
: m_browser(browser)
, m_profile(profile)
, m_tabWidget(new TabWidget(profile, this))
- , m_progressBar(new QProgressBar(this))
+ , m_progressBar(nullptr)
, m_historyBackAction(nullptr)
, m_historyForwardAction(nullptr)
, m_stopAction(nullptr)
@@ -83,49 +83,59 @@ BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile)
setAttribute(Qt::WA_DeleteOnClose, true);
setFocusPolicy(Qt::ClickFocus);
- QToolBar *toolbar = createToolBar();
- addToolBar(toolbar);
- menuBar()->addMenu(createFileMenu(m_tabWidget));
- menuBar()->addMenu(createEditMenu());
- menuBar()->addMenu(createViewMenu(toolbar));
- menuBar()->addMenu(createWindowMenu(m_tabWidget));
- menuBar()->addMenu(createHelpMenu());
+ if (!forDevTools) {
+ m_progressBar = new QProgressBar(this);
+
+ QToolBar *toolbar = createToolBar();
+ addToolBar(toolbar);
+ menuBar()->addMenu(createFileMenu(m_tabWidget));
+ menuBar()->addMenu(createEditMenu());
+ menuBar()->addMenu(createViewMenu(toolbar));
+ menuBar()->addMenu(createWindowMenu(m_tabWidget));
+ menuBar()->addMenu(createHelpMenu());
+ }
QWidget *centralWidget = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout;
layout->setSpacing(0);
layout->setMargin(0);
- addToolBarBreak();
+ if (!forDevTools) {
+ addToolBarBreak();
- m_progressBar->setMaximumHeight(1);
- m_progressBar->setTextVisible(false);
- m_progressBar->setStyleSheet(QStringLiteral("QProgressBar {border: 0px} QProgressBar::chunk {background-color: #da4453}"));
+ m_progressBar->setMaximumHeight(1);
+ m_progressBar->setTextVisible(false);
+ m_progressBar->setStyleSheet(QStringLiteral("QProgressBar {border: 0px} QProgressBar::chunk {background-color: #da4453}"));
+
+ layout->addWidget(m_progressBar);
+ }
- layout->addWidget(m_progressBar);
layout->addWidget(m_tabWidget);
centralWidget->setLayout(layout);
setCentralWidget(centralWidget);
connect(m_tabWidget, &TabWidget::titleChanged, this, &BrowserWindow::handleWebViewTitleChanged);
- connect(m_tabWidget, &TabWidget::linkHovered, [this](const QString& url) {
- statusBar()->showMessage(url);
- });
- connect(m_tabWidget, &TabWidget::loadProgress, this, &BrowserWindow::handleWebViewLoadProgress);
- connect(m_tabWidget, &TabWidget::webActionEnabledChanged, this, &BrowserWindow::handleWebActionEnabledChanged);
- connect(m_tabWidget, &TabWidget::urlChanged, [this](const QUrl &url) {
- m_urlLineEdit->setText(url.toDisplayString());
- });
- connect(m_tabWidget, &TabWidget::favIconChanged, m_favAction, &QAction::setIcon);
- connect(m_urlLineEdit, &QLineEdit::returnPressed, [this]() {
- m_tabWidget->setUrl(QUrl::fromUserInput(m_urlLineEdit->text()));
- });
+ if (!forDevTools) {
+ connect(m_tabWidget, &TabWidget::linkHovered, [this](const QString& url) {
+ statusBar()->showMessage(url);
+ });
+ connect(m_tabWidget, &TabWidget::loadProgress, this, &BrowserWindow::handleWebViewLoadProgress);
+ connect(m_tabWidget, &TabWidget::webActionEnabledChanged, this, &BrowserWindow::handleWebActionEnabledChanged);
+ connect(m_tabWidget, &TabWidget::urlChanged, [this](const QUrl &url) {
+ m_urlLineEdit->setText(url.toDisplayString());
+ });
+ connect(m_tabWidget, &TabWidget::favIconChanged, m_favAction, &QAction::setIcon);
+ connect(m_tabWidget, &TabWidget::devToolsRequested, this, &BrowserWindow::handleDevToolsRequested);
+ connect(m_urlLineEdit, &QLineEdit::returnPressed, [this]() {
+ m_tabWidget->setUrl(QUrl::fromUserInput(m_urlLineEdit->text()));
+ });
- QAction *focusUrlLineEditAction = new QAction(this);
- addAction(focusUrlLineEditAction);
- focusUrlLineEditAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_L));
- connect(focusUrlLineEditAction, &QAction::triggered, this, [this] () {
- m_urlLineEdit->setFocus(Qt::ShortcutFocusReason);
- });
+ QAction *focusUrlLineEditAction = new QAction(this);
+ addAction(focusUrlLineEditAction);
+ focusUrlLineEditAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_L));
+ connect(focusUrlLineEditAction, &QAction::triggered, this, [this] () {
+ m_urlLineEdit->setFocus(Qt::ShortcutFocusReason);
+ });
+ }
handleWebViewTitleChanged(QString());
m_tabWidget->createTab();
@@ -504,3 +514,9 @@ void BrowserWindow::handleShowWindowTriggered()
windows.at(offset)->currentTab()->setFocus();
}
}
+
+void BrowserWindow::handleDevToolsRequested(QWebEnginePage *source)
+{
+ source->setDevToolsPage(m_browser->createDevToolsWindow()->currentTab()->page());
+ source->triggerAction(QWebEnginePage::InspectElement);
+}
diff --git a/examples/webenginewidgets/simplebrowser/browserwindow.h b/examples/webenginewidgets/simplebrowser/browserwindow.h
index b522a6b9d..8f328b751 100644
--- a/examples/webenginewidgets/simplebrowser/browserwindow.h
+++ b/examples/webenginewidgets/simplebrowser/browserwindow.h
@@ -69,7 +69,7 @@ class BrowserWindow : public QMainWindow
Q_OBJECT
public:
- BrowserWindow(Browser *browser, QWebEngineProfile *profile);
+ BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool forDevTools = false);
QSize sizeHint() const override;
TabWidget *tabWidget() const;
WebView *currentTab() const;
@@ -87,6 +87,7 @@ private slots:
void handleWebViewLoadProgress(int);
void handleWebViewTitleChanged(const QString &title);
void handleWebActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled);
+ void handleDevToolsRequested(QWebEnginePage *source);
private:
QMenu *createFileMenu(TabWidget *tabWidget);
diff --git a/examples/webenginewidgets/simplebrowser/tabwidget.cpp b/examples/webenginewidgets/simplebrowser/tabwidget.cpp
index e7376c7a5..8b458a9af 100644
--- a/examples/webenginewidgets/simplebrowser/tabwidget.cpp
+++ b/examples/webenginewidgets/simplebrowser/tabwidget.cpp
@@ -190,6 +190,7 @@ void TabWidget::setupView(WebView *webView)
if (index >= 0)
closeTab(index);
});
+ connect(webView, &WebView::devToolsRequested, this, &TabWidget::devToolsRequested);
}
WebView *TabWidget::createTab()
diff --git a/examples/webenginewidgets/simplebrowser/tabwidget.h b/examples/webenginewidgets/simplebrowser/tabwidget.h
index 5b09f2708..bf83781df 100644
--- a/examples/webenginewidgets/simplebrowser/tabwidget.h
+++ b/examples/webenginewidgets/simplebrowser/tabwidget.h
@@ -77,6 +77,7 @@ signals:
void urlChanged(const QUrl &url);
void favIconChanged(const QIcon &icon);
void webActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled);
+ void devToolsRequested(QWebEnginePage *source);
public slots:
// current tab/page slots
diff --git a/examples/webenginewidgets/simplebrowser/webview.cpp b/examples/webenginewidgets/simplebrowser/webview.cpp
index 868cca037..ab42c4a0a 100644
--- a/examples/webenginewidgets/simplebrowser/webview.cpp
+++ b/examples/webenginewidgets/simplebrowser/webview.cpp
@@ -167,6 +167,7 @@ QWebEngineView *WebView::createWindow(QWebEnginePage::WebWindowType type)
}
case QWebEnginePage::WebDialog: {
WebPopupWindow *popup = new WebPopupWindow(page()->profile());
+ connect(popup->view(), &WebView::devToolsRequested, this, &WebView::devToolsRequested);
return popup->view();
}
}
@@ -185,6 +186,13 @@ void WebView::contextMenuEvent(QContextMenuEvent *event)
menu->insertAction(before, page()->action(QWebEnginePage::OpenLinkInNewWindow));
menu->insertAction(before, page()->action(QWebEnginePage::OpenLinkInNewTab));
}
+ it = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::InspectElement));
+ if (it == actions.cend()) {
+ QAction *action = new QAction(menu);
+ action->setText("Inspect Element");
+ connect(action, &QAction::triggered, [this]() { emit devToolsRequested(page()); });
+ menu->addAction(action);
+ }
menu->popup(event->globalPos());
}
diff --git a/examples/webenginewidgets/simplebrowser/webview.h b/examples/webenginewidgets/simplebrowser/webview.h
index 7276ab1c4..8559a68b8 100644
--- a/examples/webenginewidgets/simplebrowser/webview.h
+++ b/examples/webenginewidgets/simplebrowser/webview.h
@@ -75,6 +75,7 @@ protected:
signals:
void webActionEnabledChanged(QWebEnginePage::WebAction webAction, bool enabled);
void favIconChanged(const QIcon &icon);
+ void devToolsRequested(QWebEnginePage *source);
private:
void createWebActionTrigger(QWebEnginePage *page, QWebEnginePage::WebAction);
diff --git a/src/3rdparty b/src/3rdparty
-Subproject 4368c7380883fd6b32d913d27a5b8f51f615068
+Subproject 3af56a2143e4fa3a10f93917ecde4ef46c2c807
diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri
index 513cd4c1f..b4dbaeb4f 100644
--- a/src/core/core_chromium.pri
+++ b/src/core/core_chromium.pri
@@ -59,6 +59,7 @@ SOURCES = \
custom_protocol_handler.cpp \
delegated_frame_node.cpp \
desktop_screen_qt.cpp \
+ devtools_frontend_qt.cpp \
devtools_manager_delegate_qt.cpp \
download_manager_delegate_qt.cpp \
favicon_manager.cpp \
@@ -135,6 +136,7 @@ HEADERS = \
custom_protocol_handler.h \
delegated_frame_node.h \
desktop_screen_qt.h \
+ devtools_frontend_qt.h \
devtools_manager_delegate_qt.h \
download_manager_delegate_qt.h \
chromium_gpu_helper.h \
diff --git a/src/core/devtools_frontend_qt.cpp b/src/core/devtools_frontend_qt.cpp
new file mode 100644
index 000000000..7330090e7
--- /dev/null
+++ b/src/core/devtools_frontend_qt.cpp
@@ -0,0 +1,519 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// based on content/shell/browser/shell_devtools_frontend.cc:
+// 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 "devtools_frontend_qt.h"
+
+#include "browser_context_qt.h"
+#include "web_contents_adapter.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/common/url_constants.h"
+#include "components/prefs/in_memory_pref_store.h"
+#include "components/prefs/json_pref_store.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/url_constants.h"
+#include "ipc/ipc_channel.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_response_writer.h"
+
+#include <QDebug>
+using namespace QtWebEngineCore;
+
+namespace {
+
+// ResponseWriter -------------------------------------------------------------
+
+class ResponseWriter : public net::URLFetcherResponseWriter {
+public:
+ ResponseWriter(base::WeakPtr<DevToolsFrontendQt> shell_devtools_, int stream_id);
+ ~ResponseWriter() override;
+
+ // URLFetcherResponseWriter overrides:
+ int Initialize(const net::CompletionCallback &callback) override;
+ int Write(net::IOBuffer *buffer, int num_bytes, const net::CompletionCallback &callback) override;
+ int Finish(int net_error, const net::CompletionCallback &callback) override;
+
+private:
+ base::WeakPtr<DevToolsFrontendQt> shell_devtools_;
+ int stream_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResponseWriter);
+};
+
+ResponseWriter::ResponseWriter(base::WeakPtr<DevToolsFrontendQt> shell_devtools, int stream_id)
+ : shell_devtools_(shell_devtools), stream_id_(stream_id)
+{}
+
+ResponseWriter::~ResponseWriter() {}
+
+int ResponseWriter::Initialize(const net::CompletionCallback& callback)
+{
+ return net::OK;
+}
+
+int ResponseWriter::Write(net::IOBuffer *buffer, int num_bytes, const net::CompletionCallback &callback)
+{
+ std::string chunk = std::string(buffer->data(), num_bytes);
+ if (!base::IsStringUTF8(chunk))
+ return num_bytes;
+
+ base::Value *id = new base::Value(stream_id_);
+ base::Value *chunkValue = new base::Value(chunk);
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&DevToolsFrontendQt::CallClientFunction, shell_devtools_,
+ "DevToolsAPI.streamWrite", base::Owned(id),
+ base::Owned(chunkValue), nullptr));
+ return num_bytes;
+}
+
+int ResponseWriter::Finish(int net_error, const net::CompletionCallback &callback)
+{
+ return net::OK;
+}
+
+static std::string GetFrontendURL()
+{
+ return chrome::kChromeUIDevToolsURL;
+}
+
+} // namespace
+
+namespace QtWebEngineCore {
+
+// This constant should be in sync with
+// the constant at devtools_ui_bindings.cc.
+const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
+
+// static
+DevToolsFrontendQt *DevToolsFrontendQt::Show(QSharedPointer<WebContentsAdapter> frontendAdapter, content::WebContents *inspectedContents)
+{
+ DCHECK(frontendAdapter);
+ DCHECK(inspectedContents);
+
+ content::WebContents *contents = frontendAdapter->webContents();
+ if (contents == inspectedContents) {
+ qWarning() << "You can not inspect youself";
+ return nullptr;
+ }
+
+ DevToolsFrontendQt *devtoolsFrontend = new DevToolsFrontendQt(frontendAdapter, inspectedContents);
+
+ if (contents->GetURL() == GURL(GetFrontendURL())) {
+ contents->GetController().Reload(content::ReloadType::ORIGINAL_REQUEST_URL, false);
+ } else {
+ content::NavigationController::LoadURLParams loadParams = content::NavigationController::LoadURLParams(GURL(GetFrontendURL()));
+ loadParams.transition_type = ui::PageTransitionFromInt(ui::PAGE_TRANSITION_AUTO_TOPLEVEL | ui::PAGE_TRANSITION_FROM_API);
+ contents->GetController().LoadURLWithParams(loadParams);
+ }
+
+ return devtoolsFrontend;
+}
+
+DevToolsFrontendQt::DevToolsFrontendQt(QSharedPointer<WebContentsAdapter> webContentsAdapter,
+ content::WebContents *inspectedContents)
+ : content::WebContentsObserver(webContentsAdapter->webContents())
+ , m_webContentsAdapter(webContentsAdapter)
+ , m_inspectedContents(inspectedContents)
+ , m_inspect_element_at_x(-1)
+ , m_inspect_element_at_y(-1)
+ , m_prefStore(nullptr)
+ , m_weakFactory(this)
+{
+ // We use a separate prefstore than BrowserContextQt, because that one is in-memory only, and this
+ // needs to be stored or it will show introduction text on every load.
+ if (web_contents()->GetBrowserContext()->IsOffTheRecord())
+ m_prefStore = std::move(scoped_refptr<PersistentPrefStore>(new InMemoryPrefStore()));
+ else
+ CreateJsonPreferences(false);
+
+ m_frontendDelegate = static_cast<WebContentsDelegateQt *>(webContentsAdapter->webContents()->GetDelegate());
+}
+
+
+DevToolsFrontendQt::~DevToolsFrontendQt()
+{
+ for (const auto &pair : m_pendingRequests)
+ delete pair.first;
+}
+
+void DevToolsFrontendQt::Activate()
+{
+ m_frontendDelegate->ActivateContents(web_contents());
+}
+
+void DevToolsFrontendQt::Focus()
+{
+ web_contents()->Focus();
+}
+
+void DevToolsFrontendQt::InspectElementAt(int x, int y)
+{
+ if (m_agentHost)
+ m_agentHost->InspectElement(this, x, y);
+ else {
+ m_inspect_element_at_x = x;
+ m_inspect_element_at_y = y;
+ }
+}
+
+void DevToolsFrontendQt::Close()
+{
+ // Don't close the webContents, it might be reused, but pretend it was
+ WebContentsDestroyed();
+}
+
+void DevToolsFrontendQt::DisconnectFromTarget()
+{
+ if (!m_agentHost)
+ return;
+ m_agentHost->DetachClient(this);
+ m_agentHost = nullptr;
+}
+
+void DevToolsFrontendQt::ReadyToCommitNavigation(content::NavigationHandle *navigationHandle)
+{
+ // ShellDevToolsFrontend does this in RenderViewCreated,
+ // but that doesn't work for us for some reason.
+ content::RenderFrameHost *frame = navigationHandle->GetRenderFrameHost();
+ if (navigationHandle->IsInMainFrame()) {
+ // If the frontend for some reason goes to some place other than devtools, stop the bindings
+ if (navigationHandle->GetURL() != GetFrontendURL())
+ m_frontendHost.reset(nullptr);
+ else
+ m_frontendHost.reset(content::DevToolsFrontendHost::Create(frame,
+ base::Bind(&DevToolsFrontendQt::HandleMessageFromDevToolsFrontend,
+ base::Unretained(this))));
+ }
+}
+
+void DevToolsFrontendQt::DocumentAvailableInMainFrame()
+{
+ if (!m_inspectedContents)
+ return;
+ // Don't call AttachClient multiple times for the same DevToolsAgentHost.
+ // Otherwise it will call AgentHostClosed which closes the DevTools window.
+ // This may happen in cases where the DevTools content fails to load.
+ scoped_refptr<content::DevToolsAgentHost> agent_host =
+ content::DevToolsAgentHost::GetOrCreateFor(m_inspectedContents);
+ if (agent_host != m_agentHost) {
+ m_agentHost = agent_host;
+ m_agentHost->AttachClient(this);
+ if (m_inspect_element_at_x != -1) {
+ m_agentHost->InspectElement(this, m_inspect_element_at_x, m_inspect_element_at_y);
+ m_inspect_element_at_x = -1;
+ m_inspect_element_at_y = -1;
+ }
+ }
+}
+
+void DevToolsFrontendQt::WebContentsDestroyed()
+{
+ if (m_inspectedContents)
+ static_cast<WebContentsDelegateQt *>(m_inspectedContents->GetDelegate())->webContentsAdapter()->devToolsFrontendDestroyed(this);
+
+ if (m_agentHost)
+ m_agentHost->DetachClient(this);
+ delete this;
+}
+
+void DevToolsFrontendQt::SetPreference(const std::string &name, const std::string &value)
+{
+ DCHECK(m_prefStore);
+ m_prefStore->SetValue(name, base::WrapUnique(new base::Value(value)), 0);
+}
+
+void DevToolsFrontendQt::RemovePreference(const std::string &name)
+{
+ DCHECK(m_prefStore);
+ m_prefStore->RemoveValue(name, 0);
+}
+
+void DevToolsFrontendQt::ClearPreferences()
+{
+ if (web_contents()->GetBrowserContext()->IsOffTheRecord())
+ m_prefStore = scoped_refptr<PersistentPrefStore>(new InMemoryPrefStore());
+ else
+ CreateJsonPreferences(true);
+}
+
+void DevToolsFrontendQt::CreateJsonPreferences(bool clear)
+{
+ content::BrowserContext *browserContext = web_contents()->GetBrowserContext();
+ DCHECK(!browserContext->IsOffTheRecord());
+ JsonPrefStore *jsonPrefStore = new JsonPrefStore(
+ browserContext->GetPath().Append(FILE_PATH_LITERAL("devtoolsprefs.json")));
+ // We effectively clear the preferences by not calling ReadPrefs
+ if (!clear)
+ jsonPrefStore->ReadPrefs();
+
+ m_prefStore = scoped_refptr<PersistentPrefStore>(jsonPrefStore);
+}
+
+void DevToolsFrontendQt::HandleMessageFromDevToolsFrontend(const std::string &message)
+{
+ if (!m_agentHost)
+ return;
+ std::string method;
+ base::ListValue *params = nullptr;
+ base::DictionaryValue *dict = nullptr;
+ std::unique_ptr<base::Value> parsed_message = base::JSONReader::Read(message);
+ if (!parsed_message || !parsed_message->GetAsDictionary(&dict) || !dict->GetString("method", &method))
+ return;
+ int request_id = 0;
+ dict->GetInteger("id", &request_id);
+ dict->GetList("params", &params);
+
+ if (method == "dispatchProtocolMessage" && params && params->GetSize() == 1) {
+ if (!m_agentHost || !m_agentHost->IsAttached())
+ return;
+ std::string protocol_message;
+ if (!params->GetString(0, &protocol_message))
+ return;
+ m_agentHost->DispatchProtocolMessage(this, protocol_message);
+ } else if (method == "loadCompleted") {
+ web_contents()->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16("DevToolsAPI.setUseSoftMenu(true);"));
+ } else if (method == "loadNetworkResource" && params->GetSize() == 3) {
+ // TODO(pfeldman): handle some of the embedder messages in content.
+ std::string url;
+ std::string headers;
+ int stream_id;
+ if (!params->GetString(0, &url) || !params->GetString(1, &headers) || !params->GetInteger(2, &stream_id))
+ return;
+
+ GURL gurl(url);
+ if (!gurl.is_valid()) {
+ base::DictionaryValue response;
+ response.SetInteger("statusCode", 404);
+ SendMessageAck(request_id, &response);
+ return;
+ }
+
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation(
+ "devtools_handle_front_end_messages", R"(
+ semantics {
+ sender: "Developer Tools"
+ description:
+ "When user opens Developer Tools, the browser may fetch "
+ "additional resources from the network to enrich the debugging "
+ "experience (e.g. source map resources)."
+ trigger: "User opens Developer Tools to debug a web page."
+ data: "Any resources requested by Developer Tools."
+ destination: OTHER
+ }
+ policy {
+ cookies_allowed: YES
+ cookies_store: "user"
+ setting:
+ "It's not possible to disable this feature from settings."
+ chrome_policy {
+ DeveloperToolsDisabled {
+ policy_options {mode: MANDATORY}
+ DeveloperToolsDisabled: true
+ }
+ }
+ })");
+ net::URLFetcher *fetcher = net::URLFetcher::Create(gurl, net::URLFetcher::GET, this, traffic_annotation).release();
+ m_pendingRequests[fetcher] = request_id;
+ fetcher->SetRequestContext(content::BrowserContext::GetDefaultStoragePartition(
+ web_contents()->GetBrowserContext())->GetURLRequestContext());
+ fetcher->SetExtraRequestHeaders(headers);
+ fetcher->SaveResponseWithWriter(std::unique_ptr<net::URLFetcherResponseWriter>(
+ new ResponseWriter(m_weakFactory.GetWeakPtr(), stream_id)));
+ fetcher->Start();
+ return;
+ } else if (method == "getPreferences") {
+ m_preferences = *m_prefStore->GetValues();
+ SendMessageAck(request_id, &m_preferences);
+ return;
+ } else if (method == "setPreference") {
+ std::string name;
+ std::string value;
+ if (!params->GetString(0, &name) || !params->GetString(1, &value))
+ return;
+ SetPreference(name, value);
+ } else if (method == "removePreference") {
+ std::string name;
+ if (!params->GetString(0, &name))
+ return;
+ RemovePreference(name);
+ } else if (method == "clearPreferences") {
+ ClearPreferences();
+ } else if (method == "requestFileSystems") {
+ web_contents()->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16("DevToolsAPI.fileSystemsLoaded([]);"));
+ } else if (method == "reattach") {
+ m_agentHost->DetachClient(this);
+ m_agentHost->AttachClient(this);
+ } else if (method == "openInNewTab") {
+ std::string urlString;
+ if (!params->GetString(0, &urlString))
+ return;
+ GURL url(urlString);
+ if (!url.is_valid())
+ return;
+ content::OpenURLParams openParams(GURL(url),
+ content::Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK,
+ false);
+ m_frontendDelegate->OpenURLFromTab(nullptr, openParams);
+ } else if (method == "bringToFront") {
+ Activate();
+ } else {
+ VLOG(1) << "Unimplemented devtools method: " << message;
+ return;
+ }
+
+ if (request_id)
+ SendMessageAck(request_id, nullptr);
+}
+
+void DevToolsFrontendQt::DispatchProtocolMessage(content::DevToolsAgentHost *agentHost, const std::string &message)
+{
+ Q_UNUSED(agentHost);
+ if (message.length() < kMaxMessageChunkSize) {
+ std::string param;
+ base::EscapeJSONString(message, true, &param);
+ std::string code = "DevToolsAPI.dispatchMessage(" + param + ");";
+ base::string16 javascript = base::UTF8ToUTF16(code);
+ web_contents()->GetMainFrame()->ExecuteJavaScript(javascript);
+ return;
+ }
+
+ size_t total_size = message.length();
+ for (size_t pos = 0; pos < message.length(); pos += kMaxMessageChunkSize) {
+ std::string param;
+ base::EscapeJSONString(message.substr(pos, kMaxMessageChunkSize), true, &param);
+ std::string code = "DevToolsAPI.dispatchMessageChunk(" + param + ","
+ + std::to_string(pos ? 0 : total_size) + ");";
+ base::string16 javascript = base::UTF8ToUTF16(code);
+ web_contents()->GetMainFrame()->ExecuteJavaScript(javascript);
+ }
+}
+
+void DevToolsFrontendQt::OnURLFetchComplete(const net::URLFetcher *source)
+{
+ // TODO(pfeldman): this is a copy of chrome's devtools_ui_bindings.cc.
+ // We should handle some of the commands including this one in content.
+ DCHECK(source);
+ PendingRequestsMap::iterator it = m_pendingRequests.find(source);
+ DCHECK(it != m_pendingRequests.end());
+
+ base::DictionaryValue response;
+ auto headers = base::MakeUnique<base::DictionaryValue>();
+ net::HttpResponseHeaders* rh = source->GetResponseHeaders();
+ response.SetInteger("statusCode", rh ? rh->response_code() : 200);
+
+ size_t iterator = 0;
+ std::string name;
+ std::string value;
+ while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value))
+ headers->SetString(name, value);
+ response.Set("headers", std::move(headers));
+
+ SendMessageAck(it->second, &response);
+ m_pendingRequests.erase(it);
+ delete source;
+}
+
+void DevToolsFrontendQt::CallClientFunction(const std::string &function_name,
+ const base::Value *arg1,
+ const base::Value *arg2,
+ const base::Value *arg3)
+{
+ std::string javascript = function_name + "(";
+ if (arg1) {
+ std::string json;
+ base::JSONWriter::Write(*arg1, &json);
+ javascript.append(json);
+ if (arg2) {
+ base::JSONWriter::Write(*arg2, &json);
+ javascript.append(", ").append(json);
+ if (arg3) {
+ base::JSONWriter::Write(*arg3, &json);
+ javascript.append(", ").append(json);
+ }
+ }
+ }
+ javascript.append(");");
+ web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(javascript));
+}
+
+void DevToolsFrontendQt::SendMessageAck(int request_id, const base::Value *arg)
+{
+ base::Value id_value(request_id);
+ CallClientFunction("DevToolsAPI.embedderMessageAck", &id_value, arg, nullptr);
+}
+
+void DevToolsFrontendQt::AgentHostClosed(content::DevToolsAgentHost *agentHost, bool /*replaced*/)
+{
+ DCHECK(agentHost == m_agentHost.get());
+ m_agentHost = nullptr;
+ m_inspectedContents = nullptr;
+ Close();
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/devtools_frontend_qt.h b/src/core/devtools_frontend_qt.h
new file mode 100644
index 000000000..7d00845ba
--- /dev/null
+++ b/src/core/devtools_frontend_qt.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEVTOOLS_FRONTEND_QT_H
+#define DEVTOOLS_FRONTEND_QT_H
+
+#include <memory>
+
+#include "web_contents_delegate_qt.h"
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/devtools_frontend_host.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+namespace base {
+class Value;
+}
+
+namespace content {
+class NavigationHandle;
+class RenderViewHost;
+class WebContents;
+} // namespace content
+
+class PersistentPrefStore;
+
+namespace QtWebEngineCore {
+
+class DevToolsFrontendQt : public content::WebContentsObserver
+ , public content::DevToolsAgentHostClient
+ , public net::URLFetcherDelegate {
+public:
+ static DevToolsFrontendQt *Show(QSharedPointer<WebContentsAdapter> frontendAdapter, content::WebContents *inspectedContents);
+
+ void Activate();
+ void Focus();
+ void InspectElementAt(int x, int y);
+ void Close();
+
+ void DisconnectFromTarget();
+
+ void CallClientFunction(const std::string& function_name,
+ const base::Value* arg1,
+ const base::Value* arg2,
+ const base::Value* arg3);
+
+ WebContentsDelegateQt *frontendDelegate() const
+ {
+ return m_frontendDelegate;
+ }
+
+protected:
+ DevToolsFrontendQt(QSharedPointer<WebContentsAdapter> webContentsAdapter, content::WebContents *inspectedContents);
+ ~DevToolsFrontendQt() override;
+
+ // content::DevToolsAgentHostClient implementation.
+ void AgentHostClosed(content::DevToolsAgentHost* agent_host, bool replaced) override;
+ void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host, const std::string& message) override;
+
+ void SetPreferences(const std::string& json);
+ virtual void HandleMessageFromDevToolsFrontend(const std::string& message);
+
+private:
+ // WebContentsObserver overrides
+ void ReadyToCommitNavigation(content::NavigationHandle* navigation_handle) override;
+ void DocumentAvailableInMainFrame() override;
+ void WebContentsDestroyed() override;
+
+ // net::URLFetcherDelegate overrides.
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ void SendMessageAck(int request_id, const base::Value* arg1);
+ void SetPreference(const std::string &name, const std::string &value);
+ void RemovePreference(const std::string &name);
+ void ClearPreferences();
+ void CreateJsonPreferences(bool clear);
+
+ // We shouldn't be keeping it alive
+ QWeakPointer<WebContentsAdapter> m_webContentsAdapter;
+ WebContentsDelegateQt *m_frontendDelegate;
+ content::WebContents *m_inspectedContents;
+ scoped_refptr<content::DevToolsAgentHost> m_agentHost;
+ int m_inspect_element_at_x;
+ int m_inspect_element_at_y;
+ std::unique_ptr<content::DevToolsFrontendHost> m_frontendHost;
+ using PendingRequestsMap = std::map<const net::URLFetcher*, int>;
+ PendingRequestsMap m_pendingRequests;
+ base::DictionaryValue m_preferences;
+ scoped_refptr<PersistentPrefStore> m_prefStore;
+ base::WeakPtrFactory<DevToolsFrontendQt> m_weakFactory;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsFrontendQt);
+};
+
+} // namespace QtWebEngineCore
+
+#endif // DEVTOOLS_FRONTEND_QT_H
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index e084de463..762f91b3c 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -487,7 +487,7 @@ bool RenderWidgetHostViewQt::HasFocus() const
bool RenderWidgetHostViewQt::IsSurfaceAvailableForCopy() const
{
- return true;
+ return false;
}
void RenderWidgetHostViewQt::Show()
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 68481dccc..515981b1b 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -48,6 +48,7 @@
#include "browser_context_adapter.h"
#include "browser_context_adapter_client.h"
#include "browser_context_qt.h"
+#include "devtools_frontend_qt.h"
#include "download_manager_delegate_qt.h"
#include "media_capture_devices_dispatcher.h"
#include "print_view_manager_qt.h"
@@ -359,6 +360,7 @@ WebContentsAdapterPrivate::WebContentsAdapterPrivate()
, nextRequestId(CallbackDirectory::ReservedCallbackIdsEnd)
, lastFindRequestId(0)
, currentDropAction(blink::kWebDragOperationNone)
+ , devToolsFrontend(nullptr)
{
}
@@ -1035,19 +1037,56 @@ void WebContentsAdapter::executeMediaPlayerActionAt(const QPoint &location, Medi
void WebContentsAdapter::inspectElementAt(const QPoint &location)
{
Q_D(WebContentsAdapter);
- if (content::DevToolsAgentHost::HasFor(d->webContents.get())) {
- content::DevToolsAgentHost::GetOrCreateFor(d->webContents.get())->InspectElement(nullptr, location.x(), location.y());
+ if (d->devToolsFrontend) {
+ d->devToolsFrontend->InspectElementAt(location.x(), location.y());
+ return;
}
+ if (content::DevToolsAgentHost::HasFor(d->webContents.get()))
+ content::DevToolsAgentHost::GetOrCreateFor(d->webContents.get())->InspectElement(nullptr, location.x(), location.y());
}
bool WebContentsAdapter::hasInspector() const
{
- const Q_D(WebContentsAdapter);
+ Q_D(const WebContentsAdapter);
+ if (d->devToolsFrontend)
+ return true;
if (content::DevToolsAgentHost::HasFor(d->webContents.get()))
return content::DevToolsAgentHost::GetOrCreateFor(d->webContents.get())->IsAttached();
return false;
}
+void WebContentsAdapter::openDevToolsFrontend(QSharedPointer<WebContentsAdapter> frontendAdapter)
+{
+ Q_D(WebContentsAdapter);
+ if (d->devToolsFrontend &&
+ d->devToolsFrontend->frontendDelegate() == frontendAdapter->webContents()->GetDelegate())
+ return;
+
+ if (d->devToolsFrontend) {
+ d->devToolsFrontend->DisconnectFromTarget();
+ d->devToolsFrontend->Close();
+ }
+
+ d->devToolsFrontend = DevToolsFrontendQt::Show(frontendAdapter, d->webContents.get());
+}
+
+void WebContentsAdapter::closeDevToolsFrontend()
+{
+ Q_D(WebContentsAdapter);
+ if (d->devToolsFrontend) {
+ d->devToolsFrontend->DisconnectFromTarget();
+ d->devToolsFrontend->Close();
+ }
+}
+
+void WebContentsAdapter::devToolsFrontendDestroyed(DevToolsFrontendQt *frontend)
+{
+ Q_D(WebContentsAdapter);
+ Q_ASSERT(frontend == d->devToolsFrontend);
+ Q_UNUSED(frontend);
+ d->devToolsFrontend = nullptr;
+}
+
void WebContentsAdapter::exitFullScreen()
{
Q_D(WebContentsAdapter);
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index 51fd2891d..9ce438119 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -67,6 +67,7 @@ QT_END_NAMESPACE
namespace QtWebEngineCore {
class BrowserContextQt;
+class DevToolsFrontendQt;
class MessagePassingInterface;
class WebContentsAdapterPrivate;
class FaviconManager;
@@ -148,6 +149,9 @@ public:
void exitFullScreen();
void requestClose();
void changedFullScreen();
+ void openDevToolsFrontend(QSharedPointer<WebContentsAdapter> devtoolsFrontend);
+ void closeDevToolsFrontend();
+ void devToolsFrontendDestroyed(DevToolsFrontendQt *frontend);
void wasShown();
void wasHidden();
diff --git a/src/core/web_contents_adapter_p.h b/src/core/web_contents_adapter_p.h
index 94f3ca08b..50096c92e 100644
--- a/src/core/web_contents_adapter_p.h
+++ b/src/core/web_contents_adapter_p.h
@@ -73,6 +73,7 @@ struct DropData;
namespace QtWebEngineCore {
class BrowserContextAdapter;
+class DevToolsFrontendQt;
class RenderViewObserverHostQt;
class WebChannelIPCTransportHost;
class WebContentsAdapterClient;
@@ -100,6 +101,7 @@ public:
gfx::Point lastDragClientPos;
gfx::Point lastDragScreenPos;
std::unique_ptr<QTemporaryDir> dndTmpDir;
+ DevToolsFrontendQt *devToolsFrontend;
};
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index 25aa9ae04..2086bea4b 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -604,4 +604,9 @@ WebEngineSettings *WebContentsDelegateQt::webEngineSettings() const {
return m_viewClient->webEngineSettings();
}
+WebContentsAdapter *WebContentsDelegateQt::webContentsAdapter() const
+{
+ return m_viewClient->webContentsAdapter();
+}
+
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h
index 87704d3c6..c056d36ab 100644
--- a/src/core/web_contents_delegate_qt.h
+++ b/src/core/web_contents_delegate_qt.h
@@ -67,6 +67,7 @@ namespace content {
namespace QtWebEngineCore {
+class WebContentsAdapter;
class WebContentsAdapterClient;
class WebEngineSettings;
@@ -152,6 +153,7 @@ public:
const SavePageInfo &savePageInfo() { return m_savePageInfo; }
WebEngineSettings *webEngineSettings() const;
+ WebContentsAdapter *webContentsAdapter() const;
private:
QWeakPointer<WebContentsAdapter> createWindow(content::WebContents *new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture);
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index 0636d8471..2721039ae 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -823,6 +823,11 @@ void QQuickWebEngineViewPrivate::adoptWebContents(WebContentsAdapter *webContent
if (m_webChannel)
adapter->setWebChannel(m_webChannel, m_webChannelWorld);
+ if (devToolsView && devToolsView->d_ptr->adapter)
+ adapter->openDevToolsFrontend(devToolsView->d_ptr->adapter);
+ else if (inspectedView && inspectedView->d_ptr->adapter)
+ inspectedView->d_ptr->adapter->openDevToolsFrontend(adapter);
+
// set initial background color if non-default
if (m_backgroundColor != Qt::white)
adapter->backgroundColorChanged();
@@ -882,6 +887,8 @@ void QQuickWebEngineViewPrivate::ensureContentsAdapter()
adapter->setWebChannel(m_webChannel, m_webChannelWorld);
if (explicitUrl.isValid())
adapter->load(explicitUrl);
+ if (inspectedView)
+ inspectedView->d_ptr->adapter->openDevToolsFrontend(adapter);
// push down the page's user scripts
Q_FOREACH (QQuickWebEngineScript *script, m_userScripts)
script->d_func()->bind(browserContextAdapter()->userResourceController(), adapter.data());
@@ -1419,6 +1426,54 @@ void QQuickWebEngineView::setWebChannelWorld(uint webChannelWorld)
Q_EMIT webChannelWorldChanged(webChannelWorld);
}
+QQuickWebEngineView *QQuickWebEngineView::inspectedView() const
+{
+ Q_D(const QQuickWebEngineView);
+ return d->inspectedView;
+}
+
+void QQuickWebEngineView::setInspectedView(QQuickWebEngineView *view)
+{
+ Q_D(QQuickWebEngineView);
+ if (d->inspectedView == view)
+ return;
+ QQuickWebEngineView *oldView = d->inspectedView;
+ d->inspectedView = nullptr;
+ if (oldView)
+ oldView->setDevToolsView(nullptr);
+ d->inspectedView = view;
+ if (view)
+ view->setDevToolsView(this);
+ Q_EMIT inspectedViewChanged();
+}
+
+QQuickWebEngineView *QQuickWebEngineView::devToolsView() const
+{
+ Q_D(const QQuickWebEngineView);
+ return d->devToolsView;
+}
+
+void QQuickWebEngineView::setDevToolsView(QQuickWebEngineView *devToolsView)
+{
+ Q_D(QQuickWebEngineView);
+ if (d->devToolsView == devToolsView)
+ return;
+ QQuickWebEngineView *oldView = d->devToolsView;
+ d->devToolsView = nullptr;
+ if (oldView)
+ oldView->setInspectedView(nullptr);
+ d->devToolsView = devToolsView;
+ if (devToolsView)
+ devToolsView->setInspectedView(this);
+ if (d->adapter) {
+ if (devToolsView)
+ d->adapter->openDevToolsFrontend(devToolsView->d_ptr->adapter);
+ else
+ d->adapter->closeDevToolsFrontend();
+ }
+ Q_EMIT devToolsViewChanged();
+}
+
void QQuickWebEngineView::grantFeaturePermission(const QUrl &securityOrigin, QQuickWebEngineView::Feature feature, bool granted)
{
if (!d_ptr->adapter)
diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h
index ee7e01f26..275503d14 100644
--- a/src/webengine/api/qquickwebengineview_p.h
+++ b/src/webengine/api/qquickwebengineview_p.h
@@ -148,6 +148,8 @@ class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineView : public QQuickItem {
Q_PROPERTY(bool recentlyAudible READ recentlyAudible NOTIFY recentlyAudibleChanged FINAL REVISION 3)
Q_PROPERTY(uint webChannelWorld READ webChannelWorld WRITE setWebChannelWorld NOTIFY webChannelWorldChanged REVISION 3 FINAL)
+ Q_PROPERTY(QQuickWebEngineView *inspectedView READ inspectedView WRITE setInspectedView NOTIFY inspectedViewChanged REVISION 7 FINAL)
+ Q_PROPERTY(QQuickWebEngineView *devToolsView READ devToolsView WRITE setDevToolsView NOTIFY devToolsViewChanged REVISION 7 FINAL)
#ifdef ENABLE_QML_TESTSUPPORT_API
Q_PROPERTY(QQuickWebEngineTestSupport *testSupport READ testSupport WRITE setTestSupport NOTIFY testSupportChanged FINAL)
#endif
@@ -500,6 +502,11 @@ public:
bool activeFocusOnPress() const;
+ void setInspectedView(QQuickWebEngineView *);
+ QQuickWebEngineView *inspectedView() const;
+ void setDevToolsView(QQuickWebEngineView *);
+ QQuickWebEngineView *devToolsView() const;
+
public Q_SLOTS:
void runJavaScript(const QString&, const QJSValue & = QJSValue());
Q_REVISION(3) void runJavaScript(const QString&, quint32 worldId, const QJSValue & = QJSValue());
@@ -557,6 +564,8 @@ Q_SIGNALS:
Q_REVISION(5) void pdfPrintingFinished(const QString &filePath, bool success);
Q_REVISION(7) void quotaPermissionRequested(const QQuickWebEngineQuotaPermissionRequest &request);
Q_REVISION(7) void geometryChangeRequested(const QRect &geometry, const QRect &frameGeometry);
+ Q_REVISION(7) void inspectedViewChanged();
+ Q_REVISION(7) void devToolsViewChanged();
#ifdef ENABLE_QML_TESTSUPPORT_API
void testSupportChanged();
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index 708725787..a30614b2f 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -54,6 +54,7 @@
#include "qquickwebengineview_p.h"
#include "web_contents_adapter_client.h"
+#include <QPointer>
#include <QScopedPointer>
#include <QSharedData>
#include <QString>
@@ -185,6 +186,8 @@ public:
QMap<quint64, QJSValue> m_callbacks;
QList<QSharedPointer<CertificateErrorController> > m_certificateErrorControllers;
QQmlWebChannel *m_webChannel;
+ QPointer<QQuickWebEngineView> inspectedView;
+ QPointer<QQuickWebEngineView> devToolsView;
uint m_webChannelWorld;
private:
diff --git a/src/webengine/doc/src/webengineview_lgpl.qdoc b/src/webengine/doc/src/webengineview_lgpl.qdoc
index 20db51793..fadce39ad 100644
--- a/src/webengine/doc/src/webengineview_lgpl.qdoc
+++ b/src/webengine/doc/src/webengineview_lgpl.qdoc
@@ -1377,3 +1377,25 @@
}
\endqml
*/
+
+/*!
+ \qmlproperty WebEngineView WebEngineView::inspectedView
+ \since QtWebEngine 1.7
+
+ The view this view is currently inspecting, if any. Setting it
+ will navigate to an internal URL with the developer tools of
+ the view set.
+
+ \sa devToolView
+*/
+
+/*!
+ \qmlproperty WebEngineView WebEngineView::devToolsView
+ \since QtWebEngine 1.7
+
+ The view currently hosting the developer tools for this view.
+ Setting it to a new view will navigate that view to an internal
+ URL with the developer tools, and bind it to this view.
+
+ \sa inspectedView
+*/
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index 4a892103d..3ca73f12d 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -832,6 +832,7 @@ QWebEnginePage::QWebEnginePage(QWebEngineProfile *profile, QObject* parent)
QWebEnginePage::~QWebEnginePage()
{
Q_D(QWebEnginePage);
+ setDevToolsPage(nullptr);
if (d->adapter)
d->adapter->stopFinding();
QWebEngineViewPrivate::bind(d->view, 0);
@@ -1996,6 +1997,94 @@ QWebEnginePage *QWebEnginePage::createWindow(WebWindowType type)
return 0;
}
+/*!
+ \since 5.11
+ Returns the page this page is inspecting, if any.
+
+ Returns \c nullptr if this page is not a developer tools page.
+
+ \sa setInspectedPage(), devToolsPage()
+*/
+
+QWebEnginePage *QWebEnginePage::inspectedPage() const
+{
+ Q_D(const QWebEnginePage);
+ return d->inspectedPage;
+}
+
+/*!
+ \since 5.11
+ Navigates this page to an internal URL that is the developer
+ tools of \a page.
+
+ This is the same as calling setDevToolsPage() on \a page
+ with \c this as argument.
+
+ \sa inspectedPage(), setDevToolsPage()
+*/
+
+void QWebEnginePage::setInspectedPage(QWebEnginePage *page)
+{
+ Q_D(QWebEnginePage);
+ if (d->inspectedPage == page)
+ return;
+ QWebEnginePage *oldPage = d->inspectedPage;
+ d->inspectedPage = nullptr;
+ if (oldPage)
+ oldPage->setDevToolsPage(nullptr);
+ d->inspectedPage = page;
+ if (page)
+ page->setDevToolsPage(this);
+}
+
+/*!
+ \since 5.11
+ Returns the pagethat is hosting the developer tools
+ of this page, if any.
+
+ Returns \c nullptr if no developer tools page is set.
+
+ \sa setDevToolsPage(), inspectedPage()
+*/
+
+QWebEnginePage *QWebEnginePage::devToolsPage() const
+{
+ Q_D(const QWebEnginePage);
+ return d->devToolsPage;
+}
+
+/*!
+ \since 5.11
+ Binds \a devToolsPage to be the developer tools of this page.
+ Triggers \a devToolsPage to navigate to an internal URL
+ with the developer tools.
+
+ This is the same as calling setInspectedPage() on \a devToolsPage
+ with \c this as argument.
+
+ \sa devToolsPage(), setInspectedPage()
+*/
+
+void QWebEnginePage::setDevToolsPage(QWebEnginePage *devToolsPage)
+{
+ Q_D(QWebEnginePage);
+ if (d->devToolsPage == devToolsPage)
+ return;
+ QWebEnginePage *oldDevTools = d->devToolsPage;
+ d->devToolsPage = nullptr;
+ if (oldDevTools)
+ oldDevTools->setInspectedPage(nullptr);
+ d->devToolsPage = devToolsPage;
+ if (devToolsPage)
+ devToolsPage->setInspectedPage(this);
+ if (d->adapter) {
+ if (devToolsPage)
+ d->adapter->openDevToolsFrontend(devToolsPage->d_ptr->adapter);
+ else
+ d->adapter->closeDevToolsFrontend();
+ }
+}
+
ASSERT_ENUMS_MATCH(FilePickerController::Open, QWebEnginePage::FileSelectOpen)
ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, QWebEnginePage::FileSelectOpenMultiple)
diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h
index 3fad6ca09..531eef6e9 100644
--- a/src/webenginewidgets/api/qwebenginepage.h
+++ b/src/webenginewidgets/api/qwebenginepage.h
@@ -316,6 +316,11 @@ public:
void print(QPrinter *printer, const QWebEngineCallback<bool> &resultCallback);
#endif // QDOC
+ void setInspectedPage(QWebEnginePage *page);
+ QWebEnginePage *inspectedPage() const;
+ void setDevToolsPage(QWebEnginePage *page);
+ QWebEnginePage *devToolsPage() const;
+
const QWebEngineContextMenuData &contextMenuData() const;
Q_SIGNALS:
diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h
index 2d44d1849..66f479717 100644
--- a/src/webenginewidgets/api/qwebenginepage_p.h
+++ b/src/webenginewidgets/api/qwebenginepage_p.h
@@ -57,7 +57,9 @@
#include "qwebenginecontextmenudata.h"
#include "qwebenginescriptcollection.h"
#include "web_contents_adapter_client.h"
+
#include <QtCore/qcompilerdetection.h>
+#include <QtCore/QPointer>
namespace QtWebEngineCore {
class RenderWidgetHostViewQtDelegate;
@@ -175,6 +177,8 @@ public:
unsigned int webChannelWorldId;
QUrl iconUrl;
bool m_navigationActionTriggered;
+ QPointer<QWebEnginePage> inspectedPage;
+ QPointer<QWebEnginePage> devToolsPage;
mutable QtWebEngineCore::CallbackDirectory m_callbacks;
mutable QAction *actions[QWebEnginePage::WebActionCount];