xref: /OK3568_Linux_fs/app/forlinx/forlinx_qt/simplebrowser/browserwindow.cpp (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 **   * Redistributions of source code must retain the above copyright
25 **     notice, this list of conditions and the following disclaimer.
26 **   * Redistributions in binary form must reproduce the above copyright
27 **     notice, this list of conditions and the following disclaimer in
28 **     the documentation and/or other materials provided with the
29 **     distribution.
30 **   * Neither the name of The Qt Company Ltd nor the names of its
31 **     contributors may be used to endorse or promote products derived
32 **     from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50 
51 #include "browser.h"
52 #include "browserwindow.h"
53 #include "downloadmanagerwidget.h"
54 #include "tabwidget.h"
55 #include "webview.h"
56 #include <QApplication>
57 #include <QCloseEvent>
58 #include <QDesktopWidget>
59 #include <QEvent>
60 #include <QFileDialog>
61 #include <QInputDialog>
62 #include <QMenuBar>
63 #include <QMessageBox>
64 #include <QProgressBar>
65 #include <QStatusBar>
66 #include <QToolBar>
67 #include <QVBoxLayout>
68 #include <QWebEngineProfile>
69 
BrowserWindow(Browser * browser,QWebEngineProfile * profile,bool forDevTools)70 BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool forDevTools)
71     : m_browser(browser)
72     , m_profile(profile)
73     , m_tabWidget(new TabWidget(profile, this))
74     , m_progressBar(nullptr)
75     , m_historyBackAction(nullptr)
76     , m_historyForwardAction(nullptr)
77     , m_stopAction(nullptr)
78     , m_reloadAction(nullptr)
79     , m_stopReloadAction(nullptr)
80     , m_urlLineEdit(nullptr)
81     , m_favAction(nullptr)
82 {
83     setAttribute(Qt::WA_DeleteOnClose, true);
84     setFocusPolicy(Qt::ClickFocus);
85 
86     if (!forDevTools) {
87         m_progressBar = new QProgressBar(this);
88 
89         QToolBar *toolbar = createToolBar();
90         addToolBar(toolbar);
91         menuBar()->addMenu(createFileMenu(m_tabWidget));
92         menuBar()->addMenu(createEditMenu());
93         menuBar()->addMenu(createViewMenu(toolbar));
94         menuBar()->addMenu(createWindowMenu(m_tabWidget));
95         menuBar()->addMenu(createHelpMenu());
96     }
97 
98     QWidget *centralWidget = new QWidget(this);
99     QVBoxLayout *layout = new QVBoxLayout;
100     layout->setSpacing(0);
101     layout->setMargin(0);
102     if (!forDevTools) {
103         addToolBarBreak();
104 
105         m_progressBar->setMaximumHeight(1);
106         m_progressBar->setTextVisible(false);
107         m_progressBar->setStyleSheet(QStringLiteral("QProgressBar {border: 0px} QProgressBar::chunk {background-color: #da4453}"));
108 
109         layout->addWidget(m_progressBar);
110     }
111 
112     layout->addWidget(m_tabWidget);
113     centralWidget->setLayout(layout);
114     setCentralWidget(centralWidget);
115 
116     connect(m_tabWidget, &TabWidget::titleChanged, this, &BrowserWindow::handleWebViewTitleChanged);
117     if (!forDevTools) {
118         connect(m_tabWidget, &TabWidget::linkHovered, [this](const QString& url) {
119             statusBar()->showMessage(url);
120         });
121         connect(m_tabWidget, &TabWidget::loadProgress, this, &BrowserWindow::handleWebViewLoadProgress);
122         connect(m_tabWidget, &TabWidget::webActionEnabledChanged, this, &BrowserWindow::handleWebActionEnabledChanged);
123         connect(m_tabWidget, &TabWidget::urlChanged, [this](const QUrl &url) {
124             m_urlLineEdit->setText(url.toDisplayString());
125         });
126         connect(m_tabWidget, &TabWidget::favIconChanged, m_favAction, &QAction::setIcon);
127         connect(m_tabWidget, &TabWidget::devToolsRequested, this, &BrowserWindow::handleDevToolsRequested);
128         connect(m_urlLineEdit, &QLineEdit::returnPressed, [this]() {
129             m_tabWidget->setUrl(QUrl::fromUserInput(m_urlLineEdit->text()));
130         });
131 
132         QAction *focusUrlLineEditAction = new QAction(this);
133         addAction(focusUrlLineEditAction);
134         focusUrlLineEditAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_L));
135         connect(focusUrlLineEditAction, &QAction::triggered, this, [this] () {
136             m_urlLineEdit->setFocus(Qt::ShortcutFocusReason);
137         });
138     }
139 
140     handleWebViewTitleChanged(QString());
141     m_tabWidget->createTab();
142 }
143 
sizeHint() const144 QSize BrowserWindow::sizeHint() const
145 {
146     QRect desktopRect = QApplication::desktop()->screenGeometry();
147     QSize size = desktopRect.size() * qreal(0.9);
148     return size;
149 }
150 
createFileMenu(TabWidget * tabWidget)151 QMenu *BrowserWindow::createFileMenu(TabWidget *tabWidget)
152 {
153     QMenu *fileMenu = new QMenu(tr("&File"));
154     fileMenu->addAction(tr("&New Window"), this, &BrowserWindow::handleNewWindowTriggered, QKeySequence::New);
155     fileMenu->addAction(tr("New &Incognito Window"), this, &BrowserWindow::handleNewIncognitoWindowTriggered);
156 
157     QAction *newTabAction = new QAction(tr("New &Tab"), this);
158     newTabAction->setShortcuts(QKeySequence::AddTab);
159     connect(newTabAction, &QAction::triggered, this, [this]() {
160         m_tabWidget->createTab();
161         m_urlLineEdit->setFocus();
162     });
163     fileMenu->addAction(newTabAction);
164 
165     fileMenu->addAction(tr("&Open File..."), this, &BrowserWindow::handleFileOpenTriggered, QKeySequence::Open);
166     fileMenu->addSeparator();
167 
168     QAction *closeTabAction = new QAction(tr("&Close Tab"), this);
169     closeTabAction->setShortcuts(QKeySequence::Close);
170     connect(closeTabAction, &QAction::triggered, [tabWidget]() {
171         tabWidget->closeTab(tabWidget->currentIndex());
172     });
173     fileMenu->addAction(closeTabAction);
174 
175     QAction *closeAction = new QAction(tr("&Quit"),this);
176     closeAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q));
177     connect(closeAction, &QAction::triggered, this, &QWidget::close);
178     fileMenu->addAction(closeAction);
179 
180     connect(fileMenu, &QMenu::aboutToShow, [this, closeAction]() {
181         if (m_browser->windows().count() == 1)
182             closeAction->setText(tr("&Quit"));
183         else
184             closeAction->setText(tr("&Close Window"));
185     });
186     return fileMenu;
187 }
188 
createEditMenu()189 QMenu *BrowserWindow::createEditMenu()
190 {
191     QMenu *editMenu = new QMenu(tr("&Edit"));
192     QAction *findAction = editMenu->addAction(tr("&Find"));
193     findAction->setShortcuts(QKeySequence::Find);
194     connect(findAction, &QAction::triggered, this, &BrowserWindow::handleFindActionTriggered);
195 
196     QAction *findNextAction = editMenu->addAction(tr("Find &Next"));
197     findNextAction->setShortcut(QKeySequence::FindNext);
198     connect(findNextAction, &QAction::triggered, [this]() {
199         if (!currentTab() || m_lastSearch.isEmpty())
200             return;
201         currentTab()->findText(m_lastSearch);
202     });
203 
204     QAction *findPreviousAction = editMenu->addAction(tr("Find &Previous"));
205     findPreviousAction->setShortcut(QKeySequence::FindPrevious);
206     connect(findPreviousAction, &QAction::triggered, [this]() {
207         if (!currentTab() || m_lastSearch.isEmpty())
208             return;
209         currentTab()->findText(m_lastSearch, QWebEnginePage::FindBackward);
210     });
211 
212     return editMenu;
213 }
214 
createViewMenu(QToolBar * toolbar)215 QMenu *BrowserWindow::createViewMenu(QToolBar *toolbar)
216 {
217     QMenu *viewMenu = new QMenu(tr("&View"));
218     m_stopAction = viewMenu->addAction(tr("&Stop"));
219     QList<QKeySequence> shortcuts;
220     shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_Period));
221     shortcuts.append(Qt::Key_Escape);
222     m_stopAction->setShortcuts(shortcuts);
223     connect(m_stopAction, &QAction::triggered, [this]() {
224         m_tabWidget->triggerWebPageAction(QWebEnginePage::Stop);
225     });
226 
227     m_reloadAction = viewMenu->addAction(tr("Reload Page"));
228     m_reloadAction->setShortcuts(QKeySequence::Refresh);
229     connect(m_reloadAction, &QAction::triggered, [this]() {
230         m_tabWidget->triggerWebPageAction(QWebEnginePage::Reload);
231     });
232 
233     QAction *zoomIn = viewMenu->addAction(tr("Zoom &In"));
234     zoomIn->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Plus));
235     connect(zoomIn, &QAction::triggered, [this]() {
236         if (currentTab())
237             currentTab()->setZoomFactor(currentTab()->zoomFactor() + 0.1);
238     });
239 
240     QAction *zoomOut = viewMenu->addAction(tr("Zoom &Out"));
241     zoomOut->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Minus));
242     connect(zoomOut, &QAction::triggered, [this]() {
243         if (currentTab())
244             currentTab()->setZoomFactor(currentTab()->zoomFactor() - 0.1);
245     });
246 
247     QAction *resetZoom = viewMenu->addAction(tr("Reset &Zoom"));
248     resetZoom->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
249     connect(resetZoom, &QAction::triggered, [this]() {
250         if (currentTab())
251             currentTab()->setZoomFactor(1.0);
252     });
253 
254 
255     viewMenu->addSeparator();
256     QAction *viewToolbarAction = new QAction(tr("Hide Toolbar"),this);
257     viewToolbarAction->setShortcut(tr("Ctrl+|"));
258     connect(viewToolbarAction, &QAction::triggered, [toolbar,viewToolbarAction]() {
259         if (toolbar->isVisible()) {
260             viewToolbarAction->setText(tr("Show Toolbar"));
261             toolbar->close();
262         } else {
263             viewToolbarAction->setText(tr("Hide Toolbar"));
264             toolbar->show();
265         }
266     });
267     viewMenu->addAction(viewToolbarAction);
268 
269     QAction *viewStatusbarAction = new QAction(tr("Hide Status Bar"), this);
270     viewStatusbarAction->setShortcut(tr("Ctrl+/"));
271     connect(viewStatusbarAction, &QAction::triggered, [this, viewStatusbarAction]() {
272         if (statusBar()->isVisible()) {
273             viewStatusbarAction->setText(tr("Show Status Bar"));
274             statusBar()->close();
275         } else {
276             viewStatusbarAction->setText(tr("Hide Status Bar"));
277             statusBar()->show();
278         }
279     });
280     viewMenu->addAction(viewStatusbarAction);
281     return viewMenu;
282 }
283 
createWindowMenu(TabWidget * tabWidget)284 QMenu *BrowserWindow::createWindowMenu(TabWidget *tabWidget)
285 {
286     QMenu *menu = new QMenu(tr("&Window"));
287 
288     QAction *nextTabAction = new QAction(tr("Show Next Tab"), this);
289     QList<QKeySequence> shortcuts;
290     shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BraceRight));
291     shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_PageDown));
292     shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BracketRight));
293     shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_Less));
294     nextTabAction->setShortcuts(shortcuts);
295     connect(nextTabAction, &QAction::triggered, tabWidget, &TabWidget::nextTab);
296 
297     QAction *previousTabAction = new QAction(tr("Show Previous Tab"), this);
298     shortcuts.clear();
299     shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BraceLeft));
300     shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_PageUp));
301     shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BracketLeft));
302     shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_Greater));
303     previousTabAction->setShortcuts(shortcuts);
304     connect(previousTabAction, &QAction::triggered, tabWidget, &TabWidget::previousTab);
305 
306     connect(menu, &QMenu::aboutToShow, [this, menu, nextTabAction, previousTabAction]() {
307         menu->clear();
308         menu->addAction(nextTabAction);
309         menu->addAction(previousTabAction);
310         menu->addSeparator();
311 
312         QVector<BrowserWindow*> windows = m_browser->windows();
313         int index(-1);
314         for (auto window : windows) {
315             QAction *action = menu->addAction(window->windowTitle(), this, &BrowserWindow::handleShowWindowTriggered);
316             action->setData(++index);
317             action->setCheckable(true);
318             if (window == this)
319                 action->setChecked(true);
320         }
321     });
322     return menu;
323 }
324 
createHelpMenu()325 QMenu *BrowserWindow::createHelpMenu()
326 {
327     QMenu *helpMenu = new QMenu(tr("&Help"));
328     helpMenu->addAction(tr("About &Qt"), qApp, QApplication::aboutQt);
329     return helpMenu;
330 }
331 
createToolBar()332 QToolBar *BrowserWindow::createToolBar()
333 {
334     QToolBar *navigationBar = new QToolBar(tr("Navigation"));
335     navigationBar->setMovable(false);
336     navigationBar->toggleViewAction()->setEnabled(false);
337 
338     m_historyBackAction = new QAction(this);
339     QList<QKeySequence> backShortcuts = QKeySequence::keyBindings(QKeySequence::Back);
340     for (auto it = backShortcuts.begin(); it != backShortcuts.end();) {
341         // Chromium already handles navigate on backspace when appropriate.
342         if ((*it)[0] == Qt::Key_Backspace)
343             it = backShortcuts.erase(it);
344         else
345             ++it;
346     }
347     // For some reason Qt doesn't bind the dedicated Back key to Back.
348     backShortcuts.append(QKeySequence(Qt::Key_Back));
349     m_historyBackAction->setShortcuts(backShortcuts);
350     m_historyBackAction->setIconVisibleInMenu(false);
351     m_historyBackAction->setIcon(QIcon(QStringLiteral(":go-previous.png")));
352     m_historyBackAction->setToolTip(tr("Go back in history"));
353     connect(m_historyBackAction, &QAction::triggered, [this]() {
354         m_tabWidget->triggerWebPageAction(QWebEnginePage::Back);
355     });
356     navigationBar->addAction(m_historyBackAction);
357 
358     m_historyForwardAction = new QAction(this);
359     QList<QKeySequence> fwdShortcuts = QKeySequence::keyBindings(QKeySequence::Forward);
360     for (auto it = fwdShortcuts.begin(); it != fwdShortcuts.end();) {
361         if (((*it)[0] & Qt::Key_unknown) == Qt::Key_Backspace)
362             it = fwdShortcuts.erase(it);
363         else
364             ++it;
365     }
366     fwdShortcuts.append(QKeySequence(Qt::Key_Forward));
367     m_historyForwardAction->setShortcuts(fwdShortcuts);
368     m_historyForwardAction->setIconVisibleInMenu(false);
369     m_historyForwardAction->setIcon(QIcon(QStringLiteral(":go-next.png")));
370     m_historyForwardAction->setToolTip(tr("Go forward in history"));
371     connect(m_historyForwardAction, &QAction::triggered, [this]() {
372         m_tabWidget->triggerWebPageAction(QWebEnginePage::Forward);
373     });
374     navigationBar->addAction(m_historyForwardAction);
375 
376     m_stopReloadAction = new QAction(this);
377     connect(m_stopReloadAction, &QAction::triggered, [this]() {
378         m_tabWidget->triggerWebPageAction(QWebEnginePage::WebAction(m_stopReloadAction->data().toInt()));
379     });
380     navigationBar->addAction(m_stopReloadAction);
381 
382     m_urlLineEdit = new QLineEdit(this);
383     m_favAction = new QAction(this);
384     m_urlLineEdit->addAction(m_favAction, QLineEdit::LeadingPosition);
385     m_urlLineEdit->setClearButtonEnabled(true);
386     navigationBar->addWidget(m_urlLineEdit);
387 
388     auto downloadsAction = new QAction(this);
389     downloadsAction->setIcon(QIcon(QStringLiteral(":go-bottom.png")));
390     downloadsAction->setToolTip(tr("Show downloads"));
391     navigationBar->addAction(downloadsAction);
392     connect(downloadsAction, &QAction::triggered, [this]() {
393         m_browser->downloadManagerWidget().show();
394     });
395 
396     return navigationBar;
397 }
398 
handleWebActionEnabledChanged(QWebEnginePage::WebAction action,bool enabled)399 void BrowserWindow::handleWebActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled)
400 {
401     switch (action) {
402     case QWebEnginePage::Back:
403         m_historyBackAction->setEnabled(enabled);
404         break;
405     case QWebEnginePage::Forward:
406         m_historyForwardAction->setEnabled(enabled);
407         break;
408     case QWebEnginePage::Reload:
409         m_reloadAction->setEnabled(enabled);
410         break;
411     case QWebEnginePage::Stop:
412         m_stopAction->setEnabled(enabled);
413         break;
414     default:
415         qWarning("Unhandled webActionChanged signal");
416     }
417 }
418 
handleWebViewTitleChanged(const QString & title)419 void BrowserWindow::handleWebViewTitleChanged(const QString &title)
420 {
421     QString suffix = m_profile->isOffTheRecord()
422         ? tr("Qt Simple Browser (Incognito)")
423         : tr("Qt Simple Browser");
424 
425     if (title.isEmpty())
426         setWindowTitle(suffix);
427     else
428         setWindowTitle(title + " - " + suffix);
429 }
430 
handleNewWindowTriggered()431 void BrowserWindow::handleNewWindowTriggered()
432 {
433     BrowserWindow *window = m_browser->createWindow();
434     window->m_urlLineEdit->setFocus();
435 }
436 
handleNewIncognitoWindowTriggered()437 void BrowserWindow::handleNewIncognitoWindowTriggered()
438 {
439     BrowserWindow *window = m_browser->createWindow(/* offTheRecord: */ true);
440     window->m_urlLineEdit->setFocus();
441 }
442 
handleFileOpenTriggered()443 void BrowserWindow::handleFileOpenTriggered()
444 {
445     QUrl url = QFileDialog::getOpenFileUrl(this, tr("Open Web Resource"), QString(),
446                                                 tr("Web Resources (*.html *.htm *.svg *.png *.gif *.svgz);;All files (*.*)"));
447     if (url.isEmpty())
448         return;
449     currentTab()->setUrl(url);
450 }
451 
handleFindActionTriggered()452 void BrowserWindow::handleFindActionTriggered()
453 {
454     if (!currentTab())
455         return;
456     bool ok = false;
457     QString search = QInputDialog::getText(this, tr("Find"),
458                                            tr("Find:"), QLineEdit::Normal,
459                                            m_lastSearch, &ok);
460     if (ok && !search.isEmpty()) {
461         m_lastSearch = search;
462         currentTab()->findText(m_lastSearch, 0, [this](bool found) {
463             if (!found)
464                 statusBar()->showMessage(tr("\"%1\" not found.").arg(m_lastSearch));
465         });
466     }
467 }
468 
closeEvent(QCloseEvent * event)469 void BrowserWindow::closeEvent(QCloseEvent *event)
470 {
471     if (m_tabWidget->count() > 1) {
472         int ret = QMessageBox::warning(this, tr("Confirm close"),
473                                        tr("Are you sure you want to close the window ?\n"
474                                           "There are %1 tabs open.").arg(m_tabWidget->count()),
475                                        QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
476         if (ret == QMessageBox::No) {
477             event->ignore();
478             return;
479         }
480     }
481     event->accept();
482     deleteLater();
483 }
484 
tabWidget() const485 TabWidget *BrowserWindow::tabWidget() const
486 {
487     return m_tabWidget;
488 }
489 
currentTab() const490 WebView *BrowserWindow::currentTab() const
491 {
492     return m_tabWidget->currentWebView();
493 }
494 
handleWebViewLoadProgress(int progress)495 void BrowserWindow::handleWebViewLoadProgress(int progress)
496 {
497     static QIcon stopIcon(QStringLiteral(":process-stop.png"));
498     static QIcon reloadIcon(QStringLiteral(":view-refresh.png"));
499 
500     if (0 < progress && progress < 100) {
501         m_stopReloadAction->setData(QWebEnginePage::Stop);
502         m_stopReloadAction->setIcon(stopIcon);
503         m_stopReloadAction->setToolTip(tr("Stop loading the current page"));
504         m_progressBar->setValue(progress);
505     } else {
506         m_stopReloadAction->setData(QWebEnginePage::Reload);
507         m_stopReloadAction->setIcon(reloadIcon);
508         m_stopReloadAction->setToolTip(tr("Reload the current page"));
509         m_progressBar->setValue(0);
510     }
511 }
512 
handleShowWindowTriggered()513 void BrowserWindow::handleShowWindowTriggered()
514 {
515     if (QAction *action = qobject_cast<QAction*>(sender())) {
516         int offset = action->data().toInt();
517         QVector<BrowserWindow*> windows = m_browser->windows();
518         windows.at(offset)->activateWindow();
519         windows.at(offset)->currentTab()->setFocus();
520     }
521 }
522 
handleDevToolsRequested(QWebEnginePage * source)523 void BrowserWindow::handleDevToolsRequested(QWebEnginePage *source)
524 {
525     source->setDevToolsPage(m_browser->createDevToolsWindow()->currentTab()->page());
526     source->triggerAction(QWebEnginePage::InspectElement);
527 }
528