1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2015 - 2021 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 **************************************************************************/
19
20 #include "QtWebEngineWebWidget.h"
21 #include "QtWebEnginePage.h"
22 #if QTWEBENGINECORE_VERSION >= 0x050D00
23 #include "QtWebEngineUrlRequestInterceptor.h"
24 #endif
25 #include "../../../../core/Application.h"
26 #include "../../../../core/BookmarksManager.h"
27 #include "../../../../core/Console.h"
28 #include "../../../../core/GesturesManager.h"
29 #include "../../../../core/NetworkManager.h"
30 #include "../../../../core/NetworkManagerFactory.h"
31 #include "../../../../core/NotesManager.h"
32 #include "../../../../core/SearchEnginesManager.h"
33 #include "../../../../core/ThemesManager.h"
34 #include "../../../../core/TransfersManager.h"
35 #include "../../../../core/UserScript.h"
36 #include "../../../../core/Utils.h"
37 #include "../../../../core/WebBackend.h"
38 #include "../../../../ui/AuthenticationDialog.h"
39 #include "../../../../ui/ContentsDialog.h"
40 #include "../../../../ui/ContentsWidget.h"
41 #include "../../../../ui/ImagePropertiesDialog.h"
42 #include "../../../../ui/MainWindow.h"
43 #include "../../../../ui/SearchEnginePropertiesDialog.h"
44 #include "../../../../ui/SourceViewerWebWidget.h"
45 #include "../../../../ui/WebsitePreferencesDialog.h"
46
47 #include <QtCore/QEventLoop>
48 #include <QtCore/QFileInfo>
49 #include <QtCore/QMimeData>
50 #include <QtCore/QTimer>
51 #include <QtGui/QClipboard>
52 #include <QtGui/QContextMenuEvent>
53 #include <QtGui/QImageWriter>
54 #if QTWEBENGINECORE_VERSION >= 0x050C00
55 #include <QtPrintSupport/QPrintPreviewDialog>
56 #endif
57 #include <QtWebEngineCore/QWebEngineCookieStore>
58 #include <QtWebEngineWidgets/QWebEngineHistory>
59 #include <QtWebEngineWidgets/QWebEngineProfile>
60 #include <QtWebEngineWidgets/QWebEngineScript>
61 #include <QtWebEngineWidgets/QWebEngineSettings>
62 #include <QtWidgets/QAction>
63 #include <QtWidgets/QVBoxLayout>
64
65 namespace Otter
66 {
67
68 #if QTWEBENGINECORE_VERSION >= 0x050B00
QtWebEngineInspectorWidget(QWebEnginePage * page,QWidget * parent)69 QtWebEngineInspectorWidget::QtWebEngineInspectorWidget(QWebEnginePage *page, QWidget *parent) : QWebEngineView(parent),
70 m_page(page)
71 {
72 setMinimumHeight(200);
73 }
74
showEvent(QShowEvent * event)75 void QtWebEngineInspectorWidget::showEvent(QShowEvent *event)
76 {
77 if (!page()->inspectedPage())
78 {
79 page()->setInspectedPage(m_page);
80 }
81
82 QWebEngineView::showEvent(event);
83 }
84 #endif
85
QtWebEngineWebWidget(const QVariantMap & parameters,WebBackend * backend,ContentsWidget * parent)86 QtWebEngineWebWidget::QtWebEngineWebWidget(const QVariantMap ¶meters, WebBackend *backend, ContentsWidget *parent) : WebWidget(parameters, backend, parent),
87 m_webView(nullptr),
88 #if QTWEBENGINECORE_VERSION >= 0x050B00
89 m_inspectorWidget(nullptr),
90 #endif
91 m_page(new QtWebEnginePage(SessionsManager::calculateOpenHints(parameters).testFlag(SessionsManager::PrivateOpen), this)),
92 #if QTWEBENGINECORE_VERSION >= 0x050D00
93 m_requestInterceptor(new QtWebEngineUrlRequestInterceptor(this)),
94 #endif
95 m_loadingState(FinishedLoadingState),
96 m_canGoForwardValue(UnknownValue),
97 m_documentLoadingProgress(0),
98 m_focusProxyTimer(0),
99 m_updateNavigationActionsTimer(0),
100 m_isClosing(false),
101 m_isEditing(false),
102 m_isFullScreen(false),
103 m_isTypedIn(false)
104 {
105 setFocusPolicy(Qt::StrongFocus);
106
107 #if QTWEBENGINECORE_VERSION >= 0x050D00
108 m_page->setUrlRequestInterceptor(m_requestInterceptor);
109 #endif
110
111 connect(m_page, &QtWebEnginePage::loadProgress, [&](int progress)
112 {
113 m_documentLoadingProgress = progress;
114
115 emit pageInformationChanged(DocumentLoadingProgressInformation, progress);
116 });
117 connect(m_page, &QtWebEnginePage::loadStarted, this, &QtWebEngineWebWidget::handleLoadStarted);
118 connect(m_page, &QtWebEnginePage::loadFinished, this, &QtWebEngineWebWidget::handleLoadFinished);
119 connect(m_page, &QtWebEnginePage::linkHovered, this, &QtWebEngineWebWidget::setStatusMessageOverride);
120 connect(m_page, &QtWebEnginePage::iconChanged, this, &QtWebEngineWebWidget::notifyIconChanged);
121 connect(m_page, &QtWebEnginePage::requestedPopupWindow, this, &QtWebEngineWebWidget::requestedPopupWindow);
122 connect(m_page, &QtWebEnginePage::aboutToNavigate, this, &QtWebEngineWebWidget::aboutToNavigate);
123 connect(m_page, &QtWebEnginePage::requestedNewWindow, this, &QtWebEngineWebWidget::requestedNewWindow);
124 connect(m_page, &QtWebEnginePage::authenticationRequired, this, &QtWebEngineWebWidget::handleAuthenticationRequired);
125 connect(m_page, &QtWebEnginePage::proxyAuthenticationRequired, this, &QtWebEngineWebWidget::handleProxyAuthenticationRequired);
126 #if QTWEBENGINECORE_VERSION >= 0x050C00
127 connect(m_page, &QtWebEnginePage::printRequested, this, &QtWebEngineWebWidget::handlePrintRequest);
128 #endif
129 connect(m_page, &QtWebEnginePage::windowCloseRequested, this, &QtWebEngineWebWidget::handleWindowCloseRequest);
130 connect(m_page, &QtWebEnginePage::fullScreenRequested, this, &QtWebEngineWebWidget::handleFullScreenRequest);
131 connect(m_page, &QtWebEnginePage::featurePermissionRequested, [&](const QUrl &url, QWebEnginePage::Feature feature)
132 {
133 notifyPermissionRequested(url, feature, false);
134 });
135 connect(m_page, &QtWebEnginePage::featurePermissionRequestCanceled, [&](const QUrl &url, QWebEnginePage::Feature feature)
136 {
137 notifyPermissionRequested(url, feature, true);
138 });
139 connect(m_page, &QtWebEnginePage::recentlyAudibleChanged, this, &QtWebEngineWebWidget::isAudibleChanged);
140 connect(m_page, &QtWebEnginePage::viewingMediaChanged, this, &QtWebEngineWebWidget::notifyNavigationActionsChanged);
141 connect(m_page, &QtWebEnginePage::titleChanged, this, &QtWebEngineWebWidget::notifyTitleChanged);
142 connect(m_page, &QtWebEnginePage::urlChanged, this, &QtWebEngineWebWidget::notifyUrlChanged);
143 connect(m_page, &QtWebEnginePage::renderProcessTerminated, this, &QtWebEngineWebWidget::notifyRenderProcessTerminated);
144 connect(m_page->action(QWebEnginePage::Redo), &QAction::changed, this, &QtWebEngineWebWidget::notifyRedoActionStateChanged);
145 connect(m_page->action(QWebEnginePage::Undo), &QAction::changed, this, &QtWebEngineWebWidget::notifyUndoActionStateChanged);
146 #if QTWEBENGINECORE_VERSION >= 0x050D00
147 connect(m_requestInterceptor, &QtWebEngineUrlRequestInterceptor::requestBlocked, this, &QtWebEngineWebWidget::requestBlocked);
148 #endif
149 }
150
~QtWebEngineWebWidget()151 QtWebEngineWebWidget::~QtWebEngineWebWidget()
152 {
153 m_isClosing = true;
154 }
155
timerEvent(QTimerEvent * event)156 void QtWebEngineWebWidget::timerEvent(QTimerEvent *event)
157 {
158 if (event->timerId() == m_focusProxyTimer)
159 {
160 if (focusWidget())
161 {
162 focusWidget()->removeEventFilter(this);
163 focusWidget()->installEventFilter(this);
164 }
165 }
166 else if (event->timerId() == m_updateNavigationActionsTimer)
167 {
168 killTimer(m_updateNavigationActionsTimer);
169
170 m_updateNavigationActionsTimer = 0;
171
172 emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::NavigationCategory});
173 }
174 else
175 {
176 WebWidget::timerEvent(event);
177 }
178 }
179
showEvent(QShowEvent * event)180 void QtWebEngineWebWidget::showEvent(QShowEvent *event)
181 {
182 WebWidget::showEvent(event);
183
184 ensureInitialized();
185
186 if (m_focusProxyTimer == 0)
187 {
188 m_focusProxyTimer = startTimer(500);
189 }
190 }
191
hideEvent(QHideEvent * event)192 void QtWebEngineWebWidget::hideEvent(QHideEvent *event)
193 {
194 WebWidget::hideEvent(event);
195
196 killTimer(m_focusProxyTimer);
197
198 m_focusProxyTimer = 0;
199 }
200
focusInEvent(QFocusEvent * event)201 void QtWebEngineWebWidget::focusInEvent(QFocusEvent *event)
202 {
203 WebWidget::focusInEvent(event);
204
205 ensureInitialized();
206
207 m_webView->setFocus();
208 }
209
ensureInitialized()210 void QtWebEngineWebWidget::ensureInitialized()
211 {
212 if (!m_webView)
213 {
214 m_webView = new QWebEngineView(this);
215 m_webView->setPage(m_page);
216 m_webView->setContextMenuPolicy(Qt::CustomContextMenu);
217 m_webView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
218 m_webView->installEventFilter(this);
219
220 QVBoxLayout *layout(new QVBoxLayout(this));
221 layout->addWidget(m_webView);
222 layout->setContentsMargins(0, 0, 0, 0);
223
224 setLayout(layout);
225 }
226 }
227
search(const QString & query,const QString & searchEngine)228 void QtWebEngineWebWidget::search(const QString &query, const QString &searchEngine)
229 {
230 QNetworkRequest request;
231 QNetworkAccessManager::Operation method;
232 QByteArray body;
233
234 if (SearchEnginesManager::setupSearchQuery(query, searchEngine, &request, &method, &body))
235 {
236 setRequestedUrl(request.url(), false, true);
237 updateOptions(request.url());
238
239 if (method == QNetworkAccessManager::PostOperation)
240 {
241 QWebEngineHttpRequest httpRequest(request.url(), QWebEngineHttpRequest::Post);
242 httpRequest.setPostData(body);
243
244 m_page->load(httpRequest);
245 }
246 else
247 {
248 setUrl(request.url(), false);
249 }
250 }
251 }
252
print(QPrinter * printer)253 void QtWebEngineWebWidget::print(QPrinter *printer)
254 {
255 QEventLoop eventLoop;
256
257 m_page->print(printer, [&](bool)
258 {
259 eventLoop.quit();
260 });
261
262 eventLoop.exec();
263 }
264
clearOptions()265 void QtWebEngineWebWidget::clearOptions()
266 {
267 WebWidget::clearOptions();
268
269 updateOptions(getUrl());
270 }
271
triggerAction(int identifier,const QVariantMap & parameters,ActionsManager::TriggerType trigger)272 void QtWebEngineWebWidget::triggerAction(int identifier, const QVariantMap ¶meters, ActionsManager::TriggerType trigger)
273 {
274 switch (identifier)
275 {
276 case ActionsManager::SaveAction:
277 if (m_page->isViewingMedia())
278 {
279 const SaveInformation information(Utils::getSavePath(suggestSaveFileName(SingleFileSaveFormat)));
280
281 if (information.canSave)
282 {
283 m_page->save(information.path, QWebEngineDownloadItem::SingleHtmlSaveFormat);
284 }
285 }
286 else
287 {
288 SaveFormat format(UnknownSaveFormat);
289 const QString path(getSavePath({SingleFileSaveFormat, CompletePageSaveFormat, MhtmlSaveFormat, PdfSaveFormat}, &format));
290
291 if (!path.isEmpty())
292 {
293 switch (format)
294 {
295 case CompletePageSaveFormat:
296 m_page->save(path, QWebEngineDownloadItem::CompleteHtmlSaveFormat);
297
298 break;
299 case MhtmlSaveFormat:
300 m_page->save(path, QWebEngineDownloadItem::MimeHtmlSaveFormat);
301
302 break;
303 case PdfSaveFormat:
304 m_page->printToPdf(path);
305
306 break;
307 default:
308 m_page->save(path, QWebEngineDownloadItem::SingleHtmlSaveFormat);
309
310 break;
311 }
312 }
313 }
314
315 break;
316 case ActionsManager::ClearTabHistoryAction:
317 setUrl(QUrl(QLatin1String("about:blank")));
318
319 m_page->history()->clear();
320
321 notifyNavigationActionsChanged();
322
323 break;
324 case ActionsManager::MuteTabMediaAction:
325 m_page->setAudioMuted(!m_page->isAudioMuted());
326
327 emit arbitraryActionsStateChanged({ActionsManager::MuteTabMediaAction});
328
329 break;
330 case ActionsManager::OpenLinkAction:
331 {
332 const SessionsManager::OpenHints hints(SessionsManager::calculateOpenHints(parameters));
333
334 if (hints == SessionsManager::DefaultOpen && !getCurrentHitTestResult().flags.testFlag(HitTestResult::IsLinkFromSelectionTest))
335 {
336 m_page->runJavaScript(parsePosition(QStringLiteral("var element = ((%1 >= 0) ? document.elementFromPoint((%1 + window.scrollX), (%2 + window.scrollX)) : document.activeElement); if (element) { element.click(); }"), getClickPosition()));
337
338 setClickPosition({});
339 }
340 else if (m_hitResult.linkUrl.isValid())
341 {
342 openUrl(m_hitResult.linkUrl, hints);
343 }
344 }
345
346 break;
347 case ActionsManager::OpenLinkInCurrentTabAction:
348 if (m_hitResult.linkUrl.isValid())
349 {
350 openUrl(m_hitResult.linkUrl, SessionsManager::CurrentTabOpen);
351 }
352
353 break;
354 case ActionsManager::OpenLinkInNewTabAction:
355 if (m_hitResult.linkUrl.isValid())
356 {
357 openUrl(m_hitResult.linkUrl, SessionsManager::calculateOpenHints(SessionsManager::NewTabOpen));
358 }
359
360 break;
361 case ActionsManager::OpenLinkInNewTabBackgroundAction:
362 if (m_hitResult.linkUrl.isValid())
363 {
364 openUrl(m_hitResult.linkUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen));
365 }
366
367 break;
368 case ActionsManager::OpenLinkInNewWindowAction:
369 if (m_hitResult.linkUrl.isValid())
370 {
371 openUrl(m_hitResult.linkUrl, SessionsManager::calculateOpenHints(SessionsManager::NewWindowOpen));
372 }
373
374 break;
375 case ActionsManager::OpenLinkInNewWindowBackgroundAction:
376 if (m_hitResult.linkUrl.isValid())
377 {
378 openUrl(m_hitResult.linkUrl, (SessionsManager::NewWindowOpen | SessionsManager::BackgroundOpen));
379 }
380
381 break;
382 case ActionsManager::OpenLinkInNewPrivateTabAction:
383 if (m_hitResult.linkUrl.isValid())
384 {
385 openUrl(m_hitResult.linkUrl, (SessionsManager::NewTabOpen | SessionsManager::PrivateOpen));
386 }
387
388 break;
389 case ActionsManager::OpenLinkInNewPrivateTabBackgroundAction:
390 if (m_hitResult.linkUrl.isValid())
391 {
392 openUrl(m_hitResult.linkUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen | SessionsManager::PrivateOpen));
393 }
394
395 break;
396 case ActionsManager::OpenLinkInNewPrivateWindowAction:
397 if (m_hitResult.linkUrl.isValid())
398 {
399 openUrl(m_hitResult.linkUrl, (SessionsManager::NewWindowOpen | SessionsManager::PrivateOpen));
400 }
401
402 break;
403 case ActionsManager::OpenLinkInNewPrivateWindowBackgroundAction:
404 if (m_hitResult.linkUrl.isValid())
405 {
406 openUrl(m_hitResult.linkUrl, (SessionsManager::NewWindowOpen | SessionsManager::BackgroundOpen | SessionsManager::PrivateOpen));
407 }
408
409 break;
410 case ActionsManager::CopyLinkToClipboardAction:
411 if (!m_hitResult.linkUrl.isEmpty())
412 {
413 Application::clipboard()->setText(m_hitResult.linkUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
414 }
415
416 break;
417 case ActionsManager::BookmarkLinkAction:
418 if (m_hitResult.linkUrl.isValid())
419 {
420 Application::triggerAction(ActionsManager::BookmarkPageAction, {{QLatin1String("url"), m_hitResult.linkUrl}, {QLatin1String("title"), m_hitResult.title}}, parentWidget(), trigger);
421 }
422
423 break;
424 case ActionsManager::SaveLinkToDiskAction:
425 startTransfer(new Transfer(m_hitResult.linkUrl.toString(), {}, (Transfer::CanNotifyOption | (isPrivate() ? Transfer::IsPrivateOption : Transfer::NoOption))));
426
427 break;
428 case ActionsManager::SaveLinkToDownloadsAction:
429 TransfersManager::addTransfer(new Transfer(m_hitResult.linkUrl.toString(), {}, (Transfer::CanNotifyOption | Transfer::CanAskForPathOption | Transfer::IsQuickTransferOption | (isPrivate() ? Transfer::IsPrivateOption : Transfer::NoOption))));
430
431 break;
432 case ActionsManager::OpenFrameAction:
433 if (m_hitResult.frameUrl.isValid())
434 {
435 openUrl(m_hitResult.frameUrl, SessionsManager::calculateOpenHints(parameters));
436 }
437
438 break;
439 case ActionsManager::OpenFrameInCurrentTabAction:
440 if (m_hitResult.frameUrl.isValid())
441 {
442 setUrl(m_hitResult.frameUrl, false);
443 }
444
445 break;
446 case ActionsManager::OpenFrameInNewTabAction:
447 if (m_hitResult.frameUrl.isValid())
448 {
449 openUrl(m_hitResult.frameUrl, SessionsManager::calculateOpenHints(SessionsManager::NewTabOpen));
450 }
451
452 break;
453 case ActionsManager::OpenFrameInNewTabBackgroundAction:
454 if (m_hitResult.frameUrl.isValid())
455 {
456 openUrl(m_hitResult.frameUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen));
457 }
458
459 break;
460 case ActionsManager::CopyFrameLinkToClipboardAction:
461 if (m_hitResult.frameUrl.isValid())
462 {
463 Application::clipboard()->setText(m_hitResult.frameUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
464 }
465
466 break;
467 case ActionsManager::ReloadFrameAction:
468 if (m_hitResult.frameUrl.isValid())
469 {
470 //TODO Improve
471 m_page->runJavaScript(QStringLiteral("var frames = document.querySelectorAll('iframe[src=\"%1\"], frame[src=\"%1\"]'); for (var i = 0; i < frames.length; ++i) { frames[i].src = ''; frames[i].src = \'%1\'; }").arg(m_hitResult.frameUrl.toString()));
472 }
473
474 break;
475 case ActionsManager::ViewFrameSourceAction:
476 if (m_hitResult.frameUrl.isValid())
477 {
478 const QString defaultEncoding(m_page->settings()->defaultTextEncoding());
479 QNetworkRequest request(m_hitResult.frameUrl);
480 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
481 request.setHeader(QNetworkRequest::UserAgentHeader, m_page->profile()->httpUserAgent());
482
483 QNetworkReply *reply(NetworkManagerFactory::getNetworkManager(isPrivate())->get(request));
484 SourceViewerWebWidget *sourceViewer(new SourceViewerWebWidget(isPrivate()));
485 sourceViewer->setRequestedUrl(QUrl(QLatin1String("view-source:") + m_hitResult.frameUrl.toString()), false);
486
487 if (!defaultEncoding.isEmpty())
488 {
489 sourceViewer->setOption(SettingsManager::Content_DefaultCharacterEncodingOption, defaultEncoding);
490 }
491
492 m_viewSourceReplies[reply] = sourceViewer;
493
494 connect(reply, &QNetworkReply::finished, this, &QtWebEngineWebWidget::handleViewSourceReplyFinished);
495
496 emit requestedNewWindow(sourceViewer, SessionsManager::DefaultOpen, {});
497 }
498
499 break;
500 case ActionsManager::OpenImageAction:
501 if (m_hitResult.imageUrl.isValid())
502 {
503 openUrl(m_hitResult.imageUrl, SessionsManager::calculateOpenHints(parameters));
504 }
505
506 break;
507 case ActionsManager::OpenImageInNewTabAction:
508 if (m_hitResult.imageUrl.isValid())
509 {
510 openUrl(m_hitResult.imageUrl, SessionsManager::calculateOpenHints(SessionsManager::NewTabOpen));
511 }
512
513 break;
514 case ActionsManager::OpenImageInNewTabBackgroundAction:
515 if (m_hitResult.imageUrl.isValid())
516 {
517 openUrl(m_hitResult.imageUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen));
518 }
519
520 break;
521 case ActionsManager::SaveImageToDiskAction:
522 if (m_hitResult.imageUrl.isValid())
523 {
524 if (m_hitResult.imageUrl.url().contains(QLatin1String(";base64,")))
525 {
526 const QString imageUrl(m_hitResult.imageUrl.url());
527 const QString imageType(imageUrl.mid(11, (imageUrl.indexOf(QLatin1Char(';')) - 11)));
528 const QString path(Utils::getSavePath(tr("file") + QLatin1Char('.') + imageType).path);
529
530 if (path.isEmpty())
531 {
532 break;
533 }
534
535 QImageWriter writer(path);
536
537 if (!writer.write(QImage::fromData(QByteArray::fromBase64(imageUrl.mid(imageUrl.indexOf(QLatin1String(";base64,")) + 7).toUtf8()), imageType.toStdString().c_str())))
538 {
539 Console::addMessage(tr("Failed to save image: %1").arg(writer.errorString()), Console::OtherCategory, Console::ErrorLevel, path, -1, getWindowIdentifier());
540 }
541 }
542 else
543 {
544 QNetworkRequest request(m_hitResult.imageUrl);
545 request.setHeader(QNetworkRequest::UserAgentHeader, m_page->profile()->httpUserAgent());
546
547 new Transfer(request, {}, (Transfer::CanAskForPathOption | Transfer::CanAutoDeleteOption | Transfer::IsPrivateOption));
548 }
549 }
550
551 break;
552 case ActionsManager::CopyImageToClipboardAction:
553 m_page->triggerAction(QWebEnginePage::CopyImageToClipboard);
554
555 break;
556 case ActionsManager::CopyImageUrlToClipboardAction:
557 if (!m_hitResult.imageUrl.isEmpty())
558 {
559 Application::clipboard()->setText(m_hitResult.imageUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
560 }
561
562 break;
563 case ActionsManager::ReloadImageAction:
564 if (!m_hitResult.imageUrl.isEmpty())
565 {
566 if (getUrl().matches(m_hitResult.imageUrl, (QUrl::NormalizePathSegments | QUrl::RemoveFragment | QUrl::StripTrailingSlash)))
567 {
568 triggerAction(ActionsManager::ReloadAndBypassCacheAction, {}, trigger);
569 }
570 else
571 {
572 //TODO Improve
573 m_page->runJavaScript(QStringLiteral("var images = document.querySelectorAll('img[src=\"%1\"]'); for (var i = 0; i < images.length; ++i) { images[i].src = ''; images[i].src = \'%1\'; }").arg(m_hitResult.imageUrl.toString()));
574 }
575 }
576
577 break;
578 case ActionsManager::ImagePropertiesAction:
579 if (m_hitResult.imageUrl.scheme() == QLatin1String("data"))
580 {
581 ImagePropertiesDialog *imagePropertiesDialog(new ImagePropertiesDialog(m_hitResult.imageUrl, {{QLatin1String("alternativeText"), m_hitResult.alternateText}, {QLatin1String("longDescription"), m_hitResult.longDescription}}, nullptr, this));
582 imagePropertiesDialog->setButtonsVisible(false);
583
584 ContentsDialog *dialog(new ContentsDialog(ThemesManager::createIcon(QLatin1String("dialog-information")), imagePropertiesDialog->windowTitle(), {}, {}, QDialogButtonBox::Close, imagePropertiesDialog, this));
585
586 connect(this, &QtWebEngineWebWidget::aboutToReload, dialog, &ContentsDialog::close);
587 connect(imagePropertiesDialog, &ImagePropertiesDialog::finished, dialog, &ContentsDialog::close);
588
589 showDialog(dialog, false);
590 }
591 else
592 {
593 m_page->runJavaScript(parsePosition(QStringLiteral("var element = ((%1 >= 0) ? document.elementFromPoint((%1 + window.scrollX), (%2 + window.scrollX)) : document.activeElement); if (element && element.tagName && element.tagName.toLowerCase() == 'img') { [element.naturalWidth, element.naturalHeight]; }"), getClickPosition()), [&](const QVariant &result)
594 {
595 QVariantMap properties({{QLatin1String("alternativeText"), m_hitResult.alternateText}, {QLatin1String("longDescription"), m_hitResult.longDescription}});
596
597 if (result.isValid())
598 {
599 properties[QLatin1String("width")] = result.toList()[0].toInt();
600 properties[QLatin1String("height")] = result.toList()[1].toInt();
601 }
602
603 ImagePropertiesDialog *imagePropertiesDialog(new ImagePropertiesDialog(m_hitResult.imageUrl, properties, nullptr, this));
604 imagePropertiesDialog->setButtonsVisible(false);
605
606 ContentsDialog dialog(ThemesManager::createIcon(QLatin1String("dialog-information")), imagePropertiesDialog->windowTitle(), {}, {}, QDialogButtonBox::Close, imagePropertiesDialog, this);
607
608 connect(this, &QtWebEngineWebWidget::aboutToReload, &dialog, &ContentsDialog::close);
609
610 showDialog(&dialog);
611 });
612 }
613
614 break;
615 case ActionsManager::SaveMediaToDiskAction:
616 if (m_hitResult.mediaUrl.isValid())
617 {
618 QNetworkRequest request(m_hitResult.mediaUrl);
619 request.setHeader(QNetworkRequest::UserAgentHeader, m_page->profile()->httpUserAgent());
620
621 new Transfer(request, {}, (Transfer::CanAskForPathOption | Transfer::CanAutoDeleteOption | Transfer::IsPrivateOption));
622 }
623
624 break;
625 case ActionsManager::CopyMediaUrlToClipboardAction:
626 if (!m_hitResult.mediaUrl.isEmpty())
627 {
628 Application::clipboard()->setText(m_hitResult.mediaUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
629 }
630
631 break;
632 case ActionsManager::MediaControlsAction:
633 m_page->triggerAction(QWebEnginePage::ToggleMediaControls, parameters.value(QLatin1String("isChecked"), !getActionState(identifier, parameters).isChecked).toBool());
634
635 break;
636 case ActionsManager::MediaLoopAction:
637 m_page->triggerAction(QWebEnginePage::ToggleMediaLoop, parameters.value(QLatin1String("isChecked"), !getActionState(identifier, parameters).isChecked).toBool());
638
639 break;
640 case ActionsManager::MediaPlayPauseAction:
641 m_page->triggerAction(QWebEnginePage::ToggleMediaPlayPause);
642
643 break;
644 case ActionsManager::MediaMuteAction:
645 m_page->triggerAction(QWebEnginePage::ToggleMediaMute);
646
647 break;
648 case ActionsManager::MediaPlaybackRateAction:
649 m_page->runJavaScript(parsePosition(QStringLiteral("var element = ((%1 >= 0) ? document.elementFromPoint((%1 + window.scrollX), (%2 + window.scrollX)) : document.activeElement); if (element) { element.playbackRate = %3; }"), getClickPosition()).arg(parameters.value(QLatin1String("rate"), 1).toReal()));
650
651 break;
652 case ActionsManager::GoBackAction:
653 m_page->triggerAction(QWebEnginePage::Back);
654
655 break;
656 case ActionsManager::GoForwardAction:
657 m_page->triggerAction(QWebEnginePage::Forward);
658
659 break;
660 case ActionsManager::GoToHistoryIndexAction:
661 if (parameters.contains(QLatin1String("index")))
662 {
663 const int index(parameters[QLatin1String("index")].toInt());
664
665 if (index >= 0 && index < m_page->history()->count())
666 {
667 m_page->history()->goToItem(m_page->history()->itemAt(index));
668 }
669 }
670
671 break;
672 case ActionsManager::RewindAction:
673 m_page->history()->goToItem(m_page->history()->itemAt(0));
674
675 break;
676 case ActionsManager::FastForwardAction:
677 m_page->runJavaScript(getFastForwardScript(true), [&](const QVariant &result)
678 {
679 const QUrl url(result.toUrl());
680
681 if (url.isValid())
682 {
683 setUrl(url);
684 }
685 else if (canGoForward())
686 {
687 m_page->triggerAction(QWebEnginePage::Forward);
688 }
689 });
690
691 break;
692 case ActionsManager::RemoveHistoryIndexAction:
693 if (parameters.contains(QLatin1String("index")))
694 {
695 const int index(parameters[QLatin1String("index")].toInt());
696
697 if (index >= 0 && index < m_page->history()->count())
698 {
699 WindowHistoryInformation history(getHistory());
700 history.entries.removeAt(index);
701
702 if (history.index >= index)
703 {
704 history.index = (history.index - 1);
705 }
706
707 setHistory(history);
708 }
709 }
710
711 break;
712 case ActionsManager::StopAction:
713 m_page->triggerAction(QWebEnginePage::Stop);
714
715 break;
716 case ActionsManager::ReloadAction:
717 emit aboutToReload();
718
719 m_page->triggerAction(QWebEnginePage::Stop);
720 m_page->triggerAction(QWebEnginePage::Reload);
721
722 break;
723 case ActionsManager::ReloadOrStopAction:
724 if (m_loadingState == OngoingLoadingState)
725 {
726 triggerAction(ActionsManager::StopAction, {}, trigger);
727 }
728 else
729 {
730 triggerAction(ActionsManager::ReloadAction, {}, trigger);
731 }
732
733 break;
734 case ActionsManager::ReloadAndBypassCacheAction:
735 m_page->triggerAction(QWebEnginePage::ReloadAndBypassCache);
736
737 break;
738 case ActionsManager::ContextMenuAction:
739 showContextMenu(getClickPosition());
740
741 break;
742 case ActionsManager::UndoAction:
743 m_page->triggerAction(QWebEnginePage::Undo);
744
745 break;
746 case ActionsManager::RedoAction:
747 m_page->triggerAction(QWebEnginePage::Redo);
748
749 break;
750 case ActionsManager::CutAction:
751 m_page->triggerAction(QWebEnginePage::Cut);
752
753 break;
754 case ActionsManager::CopyAction:
755 if (parameters.value(QLatin1String("mode")) == QLatin1String("plainText"))
756 {
757 const QString text(getSelectedText());
758
759 if (!text.isEmpty())
760 {
761 Application::clipboard()->setText(text);
762 }
763 }
764 else
765 {
766 m_page->triggerAction(QWebEnginePage::Copy);
767 }
768
769 break;
770 case ActionsManager::CopyPlainTextAction:
771 triggerAction(ActionsManager::CopyAction, {{QLatin1String("mode"), QLatin1String("plainText")}}, trigger);
772
773 break;
774 case ActionsManager::CopyAddressAction:
775 Application::clipboard()->setText(getUrl().toString());
776
777 break;
778 case ActionsManager::CopyToNoteAction:
779 NotesManager::addNote(BookmarksModel::UrlBookmark, {{BookmarksModel::UrlRole, getUrl()}, {BookmarksModel::DescriptionRole, getSelectedText()}});
780
781 break;
782 case ActionsManager::PasteAction:
783 if (parameters.contains(QLatin1String("note")))
784 {
785 const BookmarksModel::Bookmark *bookmark(NotesManager::getModel()->getBookmark(parameters[QLatin1String("note")].toULongLong()));
786
787 if (bookmark)
788 {
789 triggerAction(ActionsManager::PasteAction, {{QLatin1String("text"), bookmark->getDescription()}}, trigger);
790 }
791 }
792 else if (parameters.contains(QLatin1String("text")))
793 {
794 QMimeData *mimeData(new QMimeData());
795 const QStringList mimeTypes(Application::clipboard()->mimeData()->formats());
796
797 for (int i = 0; i < mimeTypes.count(); ++i)
798 {
799 mimeData->setData(mimeTypes.at(i), Application::clipboard()->mimeData()->data(mimeTypes.at(i)));
800 }
801
802 Application::clipboard()->setText(parameters[QLatin1String("text")].toString());
803
804 m_page->triggerAction(QWebEnginePage::Paste);
805
806 Application::clipboard()->setMimeData(mimeData);
807 }
808 else
809 {
810 m_page->triggerAction(QWebEnginePage::Paste);
811 }
812
813 break;
814 case ActionsManager::DeleteAction:
815 m_page->runJavaScript(QLatin1String("window.getSelection().deleteFromDocument()"));
816
817 break;
818 case ActionsManager::SelectAllAction:
819 m_page->triggerAction(QWebEnginePage::SelectAll);
820
821 break;
822 case ActionsManager::UnselectAction:
823 m_page->triggerAction(QWebEnginePage::Unselect);
824
825 break;
826 case ActionsManager::ClearAllAction:
827 triggerAction(ActionsManager::SelectAllAction, {}, trigger);
828 triggerAction(ActionsManager::DeleteAction, {}, trigger);
829
830 break;
831 case ActionsManager::CreateSearchAction:
832 {
833 QFile file(QLatin1String(":/modules/backends/web/qtwebengine/resources/createSearch.js"));
834
835 if (!file.open(QIODevice::ReadOnly))
836 {
837 break;
838 }
839
840 m_page->runJavaScript(parsePosition(QString(file.readAll()), getClickPosition()), [&](const QVariant &result)
841 {
842 if (result.isNull())
843 {
844 return;
845 }
846
847 const QIcon icon(getIcon());
848 const QUrl url(result.toMap().value(QLatin1String("url")).toString());
849 SearchEnginesManager::SearchEngineDefinition searchEngine;
850 searchEngine.title = getTitle();
851 searchEngine.formUrl = getUrl();
852 searchEngine.icon = (icon.isNull() ? ThemesManager::createIcon(QLatin1String("edit-find")) : icon);
853 searchEngine.resultsUrl.url = (url.isEmpty() ? getUrl() : (url.isRelative() ? getUrl().resolved(url) : url)).toString();
854 searchEngine.resultsUrl.enctype = result.toMap().value(QLatin1String("enctype")).toString();
855 searchEngine.resultsUrl.method = result.toMap().value(QLatin1String("method")).toString();
856 searchEngine.resultsUrl.parameters = QUrlQuery(result.toMap().value(QLatin1String("query")).toString());
857
858 SearchEnginePropertiesDialog dialog(searchEngine, SearchEnginesManager::getSearchKeywords(), this);
859
860 if (dialog.exec() == QDialog::Accepted)
861 {
862 SearchEnginesManager::addSearchEngine(dialog.getSearchEngine());
863 }
864 });
865
866 file.close();
867 }
868
869 break;
870 case ActionsManager::ScrollToStartAction:
871 m_page->runJavaScript(QLatin1String("window.scrollTo(0, 0)"));
872
873 break;
874 case ActionsManager::ScrollToEndAction:
875 m_page->runJavaScript(QLatin1String("window.scrollTo(0, document.body.scrollHeigh)"));
876
877 break;
878 case ActionsManager::ScrollPageUpAction:
879 m_page->runJavaScript(QLatin1String("window.scrollByPages(1)"));
880
881 break;
882 case ActionsManager::ScrollPageDownAction:
883 m_page->runJavaScript(QLatin1String("window.scrollByPages(-1)"));
884
885 break;
886 case ActionsManager::ScrollPageLeftAction:
887 ensureInitialized();
888
889 m_page->runJavaScript(QStringLiteral("window.scrollBy(-%1, 0)").arg(m_webView->width()));
890
891 break;
892 case ActionsManager::ScrollPageRightAction:
893 ensureInitialized();
894
895 m_page->runJavaScript(QStringLiteral("window.scrollBy(%1, 0)").arg(m_webView->width()));
896
897 break;
898 case ActionsManager::ActivateContentAction:
899 ensureInitialized();
900
901 m_webView->setFocus();
902
903 m_page->runJavaScript(QLatin1String("var element = document.activeElement; if (element && element.tagName && (element.tagName.toLowerCase() == 'input' || element.tagName.toLowerCase() == 'textarea'))) { document.activeElement.blur(); }"));
904
905 break;
906 case ActionsManager::ViewSourceAction:
907 if (canViewSource())
908 {
909 const QString defaultEncoding(m_page->settings()->defaultTextEncoding());
910 QNetworkRequest request(getUrl());
911 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
912 request.setHeader(QNetworkRequest::UserAgentHeader, m_page->profile()->httpUserAgent());
913
914 QNetworkReply *reply(NetworkManagerFactory::getNetworkManager(isPrivate())->get(request));
915 SourceViewerWebWidget *sourceViewer(new SourceViewerWebWidget(isPrivate()));
916 sourceViewer->setRequestedUrl(QUrl(QLatin1String("view-source:") + getUrl().toString()), false);
917
918 if (!defaultEncoding.isEmpty())
919 {
920 sourceViewer->setOption(SettingsManager::Content_DefaultCharacterEncodingOption, defaultEncoding);
921 }
922
923 m_viewSourceReplies[reply] = sourceViewer;
924
925 connect(reply, &QNetworkReply::finished, this, &QtWebEngineWebWidget::handleViewSourceReplyFinished);
926
927 emit requestedNewWindow(sourceViewer, SessionsManager::DefaultOpen, {});
928 }
929
930 break;
931 #if QTWEBENGINECORE_VERSION >= 0x050B00
932 case ActionsManager::InspectPageAction:
933 {
934 const bool showInspector(parameters.value(QLatin1String("isChecked"), !getActionState(identifier, parameters).isChecked).toBool());
935
936 if (showInspector && !m_inspectorWidget)
937 {
938 getInspector();
939 }
940
941 emit requestedInspectorVisibilityChange(showInspector);
942 emit arbitraryActionsStateChanged({ActionsManager::InspectPageAction});
943 }
944
945 break;
946 case ActionsManager::InspectElementAction:
947 triggerAction(ActionsManager::InspectPageAction, {{QLatin1String("isChecked"), true}}, trigger);
948
949 m_page->triggerAction(QWebEnginePage::InspectElement);
950
951 break;
952 #endif
953 case ActionsManager::FullScreenAction:
954 {
955 const MainWindow *mainWindow(MainWindow::findMainWindow(this));
956
957 if (mainWindow && !mainWindow->isFullScreen())
958 {
959 m_page->triggerAction(QWebEnginePage::ExitFullScreen);
960 }
961 }
962
963 break;
964 case ActionsManager::WebsitePreferencesAction:
965 {
966 const QUrl url(getUrl());
967 WebsitePreferencesDialog dialog(Utils::extractHost(url), {}, this);
968
969 if (dialog.exec() == QDialog::Accepted)
970 {
971 updateOptions(url);
972
973 const QVector<QNetworkCookie> cookiesToDelete(dialog.getCookiesToDelete());
974
975 for (int i = 0; i < cookiesToDelete.count(); ++i)
976 {
977 m_page->profile()->cookieStore()->deleteCookie(cookiesToDelete.at(i));
978 }
979
980 const QVector<QNetworkCookie> cookiesToInsert(dialog.getCookiesToInsert());
981
982 for (int i = 0; i < cookiesToInsert.count(); ++i)
983 {
984 m_page->profile()->cookieStore()->setCookie(cookiesToInsert.at(i));
985 }
986 }
987 }
988
989 break;
990 default:
991 break;
992 }
993 }
994
handleLoadStarted()995 void QtWebEngineWebWidget::handleLoadStarted()
996 {
997 m_lastUrlClickTime = {};
998 m_metaData.clear();
999 m_styleSheets.clear();
1000 m_feeds.clear();
1001 m_links.clear();
1002 m_searchEngines.clear();
1003 m_watchedChanges.clear();
1004 m_loadingState = OngoingLoadingState;
1005 m_documentLoadingProgress = 0;
1006
1007 setStatusMessage({});
1008 setStatusMessageOverride({});
1009
1010 emit geometryChanged();
1011 emit loadingStateChanged(OngoingLoadingState);
1012 emit pageInformationChanged(DocumentLoadingProgressInformation, 0);
1013 }
1014
handleLoadFinished()1015 void QtWebEngineWebWidget::handleLoadFinished()
1016 {
1017 m_loadingState = FinishedLoadingState;
1018 m_canGoForwardValue = UnknownValue;
1019
1020 notifyNavigationActionsChanged();
1021 startReloadTimer();
1022
1023 QTimer::singleShot(100, this, [&]()
1024 {
1025 m_page->runJavaScript(getFastForwardScript(false), [&](const QVariant &result)
1026 {
1027 m_canGoForwardValue = (result.toBool() ? TrueValue : FalseValue);
1028
1029 emit arbitraryActionsStateChanged({ActionsManager::FastForwardAction});
1030 });
1031
1032 const QVector<ChangeWatcher> watchers({FeedsWatcher, LinksWatcher, MetaDataWatcher, SearchEnginesWatcher, StylesheetsWatcher});
1033
1034 for (int i = 0; i < watchers.count(); ++i)
1035 {
1036 if (isWatchingChanges(watchers.at(i)))
1037 {
1038 updateWatchedData(watchers.at(i));
1039 }
1040 }
1041 });
1042
1043 emit contentStateChanged(getContentState());
1044 emit loadingStateChanged(FinishedLoadingState);
1045 }
1046
handleViewSourceReplyFinished()1047 void QtWebEngineWebWidget::handleViewSourceReplyFinished()
1048 {
1049 QNetworkReply *reply(qobject_cast<QNetworkReply*>(sender()));
1050
1051 if (reply)
1052 {
1053 if (reply->error() == QNetworkReply::NoError && m_viewSourceReplies.contains(reply) && m_viewSourceReplies[reply])
1054 {
1055 m_viewSourceReplies[reply]->setContents(reply->readAll(), reply->header(QNetworkRequest::ContentTypeHeader).toString());
1056 }
1057
1058 m_viewSourceReplies.remove(reply);
1059
1060 reply->deleteLater();
1061 }
1062 }
1063
1064 #if QTWEBENGINECORE_VERSION >= 0x050C00
handlePrintRequest()1065 void QtWebEngineWebWidget::handlePrintRequest()
1066 {
1067 QPrintPreviewDialog printPreviewDialog(this);
1068 printPreviewDialog.setWindowFlags(printPreviewDialog.windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint);
1069 printPreviewDialog.setWindowTitle(tr("Print Preview"));
1070
1071 if (Application::getActiveWindow())
1072 {
1073 printPreviewDialog.resize(Application::getActiveWindow()->size());
1074 }
1075
1076 connect(&printPreviewDialog, &QPrintPreviewDialog::paintRequested, this, [&](QPrinter *printer)
1077 {
1078 QEventLoop eventLoop;
1079
1080 m_page->print(printer, [&](bool)
1081 {
1082 eventLoop.quit();
1083 });
1084
1085 eventLoop.exec();
1086 });
1087
1088 printPreviewDialog.exec();
1089 }
1090 #endif
1091
handleAuthenticationRequired(const QUrl & url,QAuthenticator * authenticator)1092 void QtWebEngineWebWidget::handleAuthenticationRequired(const QUrl &url, QAuthenticator *authenticator)
1093 {
1094 AuthenticationDialog *authenticationDialog(new AuthenticationDialog(url, authenticator, AuthenticationDialog::HttpAuthentication, this));
1095 authenticationDialog->setButtonsVisible(false);
1096
1097 ContentsDialog dialog(ThemesManager::createIcon(QLatin1String("dialog-password")), authenticationDialog->windowTitle(), {}, {}, (QDialogButtonBox::Ok | QDialogButtonBox::Cancel), authenticationDialog, this);
1098
1099 connect(&dialog, &ContentsDialog::accepted, authenticationDialog, &AuthenticationDialog::accept);
1100 connect(this, &QtWebEngineWebWidget::aboutToReload, &dialog, &ContentsDialog::close);
1101
1102 showDialog(&dialog);
1103 }
1104
handleProxyAuthenticationRequired(const QUrl & url,QAuthenticator * authenticator,const QString & proxy)1105 void QtWebEngineWebWidget::handleProxyAuthenticationRequired(const QUrl &url, QAuthenticator *authenticator, const QString &proxy)
1106 {
1107 Q_UNUSED(url)
1108
1109 AuthenticationDialog *authenticationDialog(new AuthenticationDialog(proxy, authenticator, AuthenticationDialog::ProxyAuthentication, this));
1110 authenticationDialog->setButtonsVisible(false);
1111
1112 ContentsDialog dialog(ThemesManager::createIcon(QLatin1String("dialog-password")), authenticationDialog->windowTitle(), {}, {}, (QDialogButtonBox::Ok | QDialogButtonBox::Cancel), authenticationDialog, this);
1113
1114 connect(&dialog, &ContentsDialog::accepted, authenticationDialog, &AuthenticationDialog::accept);
1115 connect(this, &QtWebEngineWebWidget::aboutToReload, &dialog, &ContentsDialog::close);
1116
1117 showDialog(&dialog);
1118 }
1119
handleFullScreenRequest(QWebEngineFullScreenRequest request)1120 void QtWebEngineWebWidget::handleFullScreenRequest(QWebEngineFullScreenRequest request)
1121 {
1122 request.accept();
1123
1124 if (request.toggleOn())
1125 {
1126 const QString value(SettingsManager::getOption(SettingsManager::Permissions_EnableFullScreenOption, Utils::extractHost(request.origin())).toString());
1127
1128 if (value == QLatin1String("allow"))
1129 {
1130 MainWindow *mainWindow(MainWindow::findMainWindow(this));
1131
1132 if (mainWindow && !mainWindow->isFullScreen())
1133 {
1134 mainWindow->triggerAction(ActionsManager::FullScreenAction);
1135 }
1136 }
1137 else if (value == QLatin1String("ask"))
1138 {
1139 emit requestedPermission(FullScreenFeature, request.origin(), false);
1140 }
1141 }
1142 else
1143 {
1144 MainWindow *mainWindow(MainWindow::findMainWindow(this));
1145
1146 if (mainWindow && mainWindow->isFullScreen())
1147 {
1148 mainWindow->triggerAction(ActionsManager::FullScreenAction);
1149 }
1150
1151 emit requestedPermission(FullScreenFeature, request.origin(), true);
1152 }
1153
1154 m_isFullScreen = request.toggleOn();
1155
1156 emit isFullScreenChanged(m_isFullScreen);
1157 }
1158
notifyTitleChanged()1159 void QtWebEngineWebWidget::notifyTitleChanged()
1160 {
1161 emit titleChanged(getTitle());
1162 }
1163
notifyUrlChanged()1164 void QtWebEngineWebWidget::notifyUrlChanged()
1165 {
1166 const QUrl &url(getUrl());
1167
1168 notifyNavigationActionsChanged();
1169 updateOptions(url);
1170
1171 emit iconChanged(getIcon());
1172 emit urlChanged((url.toString() == QLatin1String("about:blank")) ? m_page->requestedUrl() : url);
1173 emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::PageCategory});
1174
1175 SessionsManager::markSessionAsModified();
1176 }
1177
notifyIconChanged()1178 void QtWebEngineWebWidget::notifyIconChanged()
1179 {
1180 emit iconChanged(getIcon());
1181 }
1182
notifyPermissionRequested(const QUrl & url,QWebEnginePage::Feature nativeFeature,bool cancel)1183 void QtWebEngineWebWidget::notifyPermissionRequested(const QUrl &url, QWebEnginePage::Feature nativeFeature, bool cancel)
1184 {
1185 FeaturePermission feature(UnknownFeature);
1186
1187 switch (nativeFeature)
1188 {
1189 case QWebEnginePage::Geolocation:
1190 feature = GeolocationFeature;
1191
1192 break;
1193 case QWebEnginePage::MediaAudioCapture:
1194 feature = CaptureAudioFeature;
1195
1196 break;
1197 case QWebEnginePage::MediaVideoCapture:
1198 feature = CaptureVideoFeature;
1199
1200 break;
1201 case QWebEnginePage::MediaAudioVideoCapture:
1202 feature = CaptureAudioVideoFeature;
1203
1204 break;
1205 case QWebEnginePage::Notifications:
1206 feature = NotificationsFeature;
1207
1208 break;
1209 case QWebEnginePage::MouseLock:
1210 feature = PointerLockFeature;
1211
1212 break;
1213 default:
1214 return;
1215 }
1216
1217 if (cancel)
1218 {
1219 emit requestedPermission(feature, url, true);
1220 }
1221 else
1222 {
1223 switch (getPermission(feature, url))
1224 {
1225 case GrantedPermission:
1226 m_page->setFeaturePermission(url, nativeFeature, QWebEnginePage::PermissionGrantedByUser);
1227
1228 break;
1229 case DeniedPermission:
1230 m_page->setFeaturePermission(url, nativeFeature, QWebEnginePage::PermissionDeniedByUser);
1231
1232 break;
1233 default:
1234 emit requestedPermission(feature, url, false);
1235
1236 break;
1237 }
1238 }
1239 }
1240
notifyRenderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus status)1241 void QtWebEngineWebWidget::notifyRenderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus status)
1242 {
1243 if (status != QWebEnginePage::NormalTerminationStatus)
1244 {
1245 m_loadingState = CrashedLoadingState;
1246
1247 emit loadingStateChanged(CrashedLoadingState);
1248 }
1249 }
1250
notifyNavigationActionsChanged()1251 void QtWebEngineWebWidget::notifyNavigationActionsChanged()
1252 {
1253 if (m_updateNavigationActionsTimer == 0)
1254 {
1255 m_updateNavigationActionsTimer = startTimer(0);
1256 }
1257 }
1258
notifyWatchedDataChanged(ChangeWatcher watcher)1259 void QtWebEngineWebWidget::notifyWatchedDataChanged(ChangeWatcher watcher)
1260 {
1261 if (m_isClosing)
1262 {
1263 return;
1264 }
1265
1266 if (watcher >= m_watchedChanges.count())
1267 {
1268 m_watchedChanges.resize(watcher + 1);
1269 }
1270
1271 m_watchedChanges[watcher] = true;
1272
1273 emit watchedDataChanged(watcher);
1274 }
1275
updateOptions(const QUrl & url)1276 void QtWebEngineWebWidget::updateOptions(const QUrl &url)
1277 {
1278 const QString encoding(getOption(SettingsManager::Content_DefaultCharacterEncodingOption, url).toString());
1279 QWebEngineSettings *settings(m_page->settings());
1280 settings->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, getOption(SettingsManager::Security_AllowMixedContentOption, url).toBool());
1281 settings->setAttribute(QWebEngineSettings::AutoLoadImages, (getOption(SettingsManager::Permissions_EnableImagesOption, url).toString() != QLatin1String("onlyCached")));
1282 settings->setAttribute(QWebEngineSettings::JavascriptEnabled, getOption(SettingsManager::Permissions_EnableJavaScriptOption, url).toBool());
1283 settings->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, getOption(SettingsManager::Permissions_ScriptsCanAccessClipboardOption, url).toBool());
1284 settings->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, (getOption(SettingsManager::Permissions_ScriptsCanOpenWindowsOption, url).toString() != QLatin1String("blockAll")));
1285 settings->setAttribute(QWebEngineSettings::LocalStorageEnabled, getOption(SettingsManager::Permissions_EnableLocalStorageOption, url).toBool());
1286 #if QTWEBENGINECORE_VERSION >= 0x050A00
1287 settings->setAttribute(QWebEngineSettings::ShowScrollBars, getOption(SettingsManager::Interface_ShowScrollBarsOption, url).toBool());
1288 #endif
1289 settings->setAttribute(QWebEngineSettings::WebGLEnabled, getOption(SettingsManager::Permissions_EnableWebglOption, url).toBool());
1290 settings->setDefaultTextEncoding((encoding == QLatin1String("auto")) ? QString() : encoding);
1291
1292 m_page->profile()->setHttpUserAgent(getBackend()->getUserAgent(NetworkManagerFactory::getUserAgent(getOption(SettingsManager::Network_UserAgentOption, url).toString()).value));
1293
1294 disconnect(m_page, &QtWebEnginePage::geometryChangeRequested, this, &QtWebEngineWebWidget::requestedGeometryChange);
1295
1296 if (getOption(SettingsManager::Permissions_ScriptsCanChangeWindowGeometryOption, url).toBool())
1297 {
1298 connect(m_page, &QtWebEnginePage::geometryChangeRequested, this, &QtWebEngineWebWidget::requestedGeometryChange);
1299 }
1300
1301 #if QTWEBENGINECORE_VERSION >= 0x050D00
1302 m_requestInterceptor->updateOptions(url);
1303 #endif
1304 }
1305
updateWatchedData(ChangeWatcher watcher)1306 void QtWebEngineWebWidget::updateWatchedData(ChangeWatcher watcher)
1307 {
1308 switch (watcher)
1309 {
1310 case FeedsWatcher:
1311 m_page->runJavaScript(m_page->createScriptSource(QLatin1String("getLinks"), {QLatin1String("a[type=\\'application/atom+xml\\'], a[type=\\'application/rss+xml\\'], link[type=\\'application/atom+xml\\'], link[type=\\'application/rss+xml\\']")}), [&](const QVariant &result)
1312 {
1313 m_feeds = processLinks(result.toList());
1314
1315 notifyWatchedDataChanged(FeedsWatcher);
1316 });
1317
1318 break;
1319 case LinksWatcher:
1320 m_page->runJavaScript(m_page->createScriptSource(QLatin1String("getLinks"), {QLatin1String("a[href]")}), [&](const QVariant &result)
1321 {
1322 m_links = processLinks(result.toList());
1323
1324 notifyWatchedDataChanged(LinksWatcher);
1325 });
1326
1327 break;
1328 case MetaDataWatcher:
1329 m_page->runJavaScript(QLatin1String("var elements = document.querySelectorAll('meta'); var metaData = []; for (var i = 0; i < elements.length; ++i) { if (elements[i].name !== '') { metaData.push({key: elements[i].name, value: elements[i].content}); } } metaData;"), [&](const QVariant &result)
1330 {
1331 QMultiMap<QString, QString> metaData;
1332 const QVariantList rawMetaData(result.toList());
1333
1334 for (int i = 0; i < rawMetaData.count(); ++i)
1335 {
1336 const QVariantHash entry(rawMetaData.at(i).toHash());
1337
1338 metaData.insertMulti(entry.value(QLatin1String("key")).toString(), entry.value(QLatin1String("value")).toString());
1339 }
1340
1341 m_metaData = metaData;
1342
1343 notifyWatchedDataChanged(MetaDataWatcher);
1344 });
1345
1346 break;
1347 case SearchEnginesWatcher:
1348 m_page->runJavaScript(m_page->createScriptSource(QLatin1String("getLinks"), {QLatin1String("link[type=\\'application/opensearchdescription+xml\\']")}), [&](const QVariant &result)
1349 {
1350 m_searchEngines = processLinks(result.toList());
1351
1352 notifyWatchedDataChanged(SearchEnginesWatcher);
1353 });
1354
1355 break;
1356 case StylesheetsWatcher:
1357 m_page->runJavaScript(m_page->createScriptSource(QLatin1String("getStyleSheets")), [&](const QVariant &result)
1358 {
1359 m_styleSheets = result.toStringList();
1360
1361 notifyWatchedDataChanged(StylesheetsWatcher);
1362 });
1363
1364 break;
1365 default:
1366 break;
1367 }
1368 }
1369
setScrollPosition(const QPoint & position)1370 void QtWebEngineWebWidget::setScrollPosition(const QPoint &position)
1371 {
1372 m_page->runJavaScript(QStringLiteral("window.scrollTo(%1, %2); [window.scrollX, window.scrollY];").arg(position.x()).arg(position.y()));
1373 }
1374
setHistory(const WindowHistoryInformation & history)1375 void QtWebEngineWebWidget::setHistory(const WindowHistoryInformation &history)
1376 {
1377 m_page->setHistory(history);
1378
1379 if (history.entries.isEmpty())
1380 {
1381 updateOptions({});
1382 }
1383 else
1384 {
1385 const QUrl url(m_page->history()->itemAt(history.index).url());
1386
1387 setRequestedUrl(url, false, true);
1388 updateOptions(url);
1389
1390 m_page->triggerAction(QWebEnginePage::Reload);
1391 }
1392
1393 notifyNavigationActionsChanged();
1394
1395 emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::PageCategory});
1396 }
1397
setHistory(QDataStream & stream)1398 void QtWebEngineWebWidget::setHistory(QDataStream &stream)
1399 {
1400 stream.device()->reset();
1401 stream >> *(m_page->history());
1402
1403 const QUrl url(m_page->history()->currentItem().url());
1404
1405 setRequestedUrl(url, false, true);
1406 updateOptions(url);
1407 }
1408
setZoom(int zoom)1409 void QtWebEngineWebWidget::setZoom(int zoom)
1410 {
1411 if (zoom != getZoom())
1412 {
1413 m_page->setZoomFactor(qBound(0.1, (static_cast<qreal>(zoom) / 100), static_cast<qreal>(100)));
1414
1415 SessionsManager::markSessionAsModified();
1416
1417 emit zoomChanged(zoom);
1418 emit geometryChanged();
1419 }
1420 }
1421
setUrl(const QUrl & url,bool isTyped)1422 void QtWebEngineWebWidget::setUrl(const QUrl &url, bool isTyped)
1423 {
1424 if (url.scheme() == QLatin1String("javascript"))
1425 {
1426 m_page->runJavaScript(url.toDisplayString(QUrl::RemoveScheme | QUrl::DecodeReserved));
1427 }
1428 else if (!url.fragment().isEmpty() && url.matches(getUrl(), (QUrl::RemoveFragment | QUrl::StripTrailingSlash | QUrl::NormalizePathSegments)))
1429 {
1430 m_page->runJavaScript(QStringLiteral("var element = document.querySelector('a[name=%1], [id=%1]'); if (element) { var geometry = element.getBoundingClientRect(); [(geometry.left + window.scrollX), (geometry.top + window.scrollY)]; }").arg(url.fragment()), [&](const QVariant &result)
1431 {
1432 if (result.isValid())
1433 {
1434 setScrollPosition(QPoint(result.toList().value(0).toInt(), result.toList().value(1).toInt()));
1435 }
1436 });
1437 }
1438 else
1439 {
1440 m_isTypedIn = isTyped;
1441
1442 const QUrl targetUrl(Utils::expandUrl(url));
1443
1444 updateOptions(targetUrl);
1445
1446 m_page->load(targetUrl);
1447
1448 notifyTitleChanged();
1449 notifyIconChanged();
1450 }
1451 }
1452
setActiveStyleSheet(const QString & styleSheet)1453 void QtWebEngineWebWidget::setActiveStyleSheet(const QString &styleSheet)
1454 {
1455 m_page->runJavaScript(QStringLiteral("var elements = document.querySelectorAll('link[rel=\\'alternate stylesheet\\']'); for (var i = 0; i < elements.length; ++i) { elements[i].disabled = (elements[i].title !== '%1'); }").arg(QString(styleSheet).replace(QLatin1Char('\''), QLatin1String("\\'"))));
1456 }
1457
setPermission(FeaturePermission feature,const QUrl & url,PermissionPolicies policies)1458 void QtWebEngineWebWidget::setPermission(FeaturePermission feature, const QUrl &url, PermissionPolicies policies)
1459 {
1460 WebWidget::setPermission(feature, url, policies);
1461
1462 if (feature == FullScreenFeature)
1463 {
1464 if (policies.testFlag(GrantedPermission))
1465 {
1466 MainWindow *mainWindow(MainWindow::findMainWindow(this));
1467
1468 if (mainWindow && !mainWindow->isFullScreen())
1469 {
1470 mainWindow->triggerAction(ActionsManager::FullScreenAction);
1471 }
1472 }
1473
1474 return;
1475 }
1476
1477 QWebEnginePage::Feature nativeFeature(QWebEnginePage::Geolocation);
1478
1479 switch (feature)
1480 {
1481 case GeolocationFeature:
1482 nativeFeature = QWebEnginePage::Geolocation;
1483
1484 break;
1485 case NotificationsFeature:
1486 nativeFeature = QWebEnginePage::Notifications;
1487
1488 break;
1489 case PointerLockFeature:
1490 nativeFeature = QWebEnginePage::MouseLock;
1491
1492 break;
1493 case CaptureAudioFeature:
1494 nativeFeature = QWebEnginePage::MediaAudioCapture;
1495
1496 break;
1497 case CaptureVideoFeature:
1498 nativeFeature = QWebEnginePage::MediaVideoCapture;
1499
1500 break;
1501 case CaptureAudioVideoFeature:
1502 nativeFeature = QWebEnginePage::MediaAudioVideoCapture;
1503
1504 break;
1505 default:
1506 return;
1507 }
1508
1509 m_page->setFeaturePermission(url, nativeFeature, (policies.testFlag(GrantedPermission) ? QWebEnginePage::PermissionGrantedByUser : QWebEnginePage::PermissionDeniedByUser));
1510 }
1511
setOption(int identifier,const QVariant & value)1512 void QtWebEngineWebWidget::setOption(int identifier, const QVariant &value)
1513 {
1514 WebWidget::setOption(identifier, value);
1515
1516 updateOptions(getUrl());
1517
1518 if (identifier == SettingsManager::Content_DefaultCharacterEncodingOption)
1519 {
1520 m_page->triggerAction(QWebEnginePage::Reload);
1521 }
1522 }
1523
setOptions(const QHash<int,QVariant> & options,const QStringList & excludedOptions)1524 void QtWebEngineWebWidget::setOptions(const QHash<int, QVariant> &options, const QStringList &excludedOptions)
1525 {
1526 WebWidget::setOptions(options, excludedOptions);
1527
1528 updateOptions(getUrl());
1529 }
1530
clone(bool cloneHistory,bool isPrivate,const QStringList & excludedOptions) const1531 WebWidget* QtWebEngineWebWidget::clone(bool cloneHistory, bool isPrivate, const QStringList &excludedOptions) const
1532 {
1533 QtWebEngineWebWidget *widget((this->isPrivate() || isPrivate) ? new QtWebEngineWebWidget({{QLatin1String("hints"), SessionsManager::PrivateOpen}}, getBackend()) : new QtWebEngineWebWidget({}, getBackend()));
1534 widget->setOptions(getOptions(), excludedOptions);
1535
1536 if (cloneHistory)
1537 {
1538 QByteArray byteArray;
1539 QDataStream stream(&byteArray, QIODevice::ReadWrite);
1540 stream << *(m_page->history());
1541
1542 widget->setHistory(stream);
1543 }
1544
1545 widget->setZoom(getZoom());
1546
1547 return widget;
1548 }
1549
1550 #if QTWEBENGINECORE_VERSION >= 0x050B00
getInspector()1551 QWidget* QtWebEngineWebWidget::getInspector()
1552 {
1553 if (!m_inspectorWidget)
1554 {
1555 m_inspectorWidget = new QtWebEngineInspectorWidget(m_page, this);
1556 }
1557
1558 return m_inspectorWidget;
1559 }
1560 #endif
1561
getViewport()1562 QWidget* QtWebEngineWebWidget::getViewport()
1563 {
1564 return (focusWidget() ? focusWidget() : m_webView);
1565 }
1566
getPage() const1567 QWebEnginePage* QtWebEngineWebWidget::getPage() const
1568 {
1569 return m_page;
1570 }
1571
parsePosition(const QString & script,const QPoint & position) const1572 QString QtWebEngineWebWidget::parsePosition(const QString &script, const QPoint &position) const
1573 {
1574 return script.arg(position.x() / m_page->zoomFactor()).arg(position.y() / m_page->zoomFactor());
1575 }
1576
getTitle() const1577 QString QtWebEngineWebWidget::getTitle() const
1578 {
1579 const QString title(m_page->title());
1580
1581 if (!title.isEmpty())
1582 {
1583 return title;
1584 }
1585
1586 const QUrl url(getUrl());
1587
1588 if (Utils::isUrlEmpty(url))
1589 {
1590 return tr("Blank Page");
1591 }
1592
1593 if (url.isLocalFile())
1594 {
1595 return QFileInfo(url.toLocalFile()).canonicalFilePath();
1596 }
1597
1598 if (!url.isEmpty())
1599 {
1600 return url.toString();
1601 }
1602
1603 return tr("(Untitled)");
1604 }
1605
getDescription() const1606 QString QtWebEngineWebWidget::getDescription() const
1607 {
1608 return m_page->runScriptSource(QLatin1String("var element = document.querySelector('[name=\\'description\\']'); var description = (element ? element.content : ''); if (description == '') { element = document.querySelector('[name=\\'og:description\\']'); description = (element ? element.property : ''); } description;")).toString();
1609 }
1610
getActiveStyleSheet() const1611 QString QtWebEngineWebWidget::getActiveStyleSheet() const
1612 {
1613 return m_page->runScriptFile(QLatin1String("getActiveStyleSheet")).toString();
1614 }
1615
getSelectedText() const1616 QString QtWebEngineWebWidget::getSelectedText() const
1617 {
1618 return m_page->selectedText();
1619 }
1620
getPageInformation(PageInformation key) const1621 QVariant QtWebEngineWebWidget::getPageInformation(PageInformation key) const
1622 {
1623 if (key == DocumentLoadingProgressInformation || key == TotalLoadingProgressInformation)
1624 {
1625 return m_documentLoadingProgress;
1626 }
1627
1628 return WebWidget::getPageInformation(key);
1629 }
1630
getUrl() const1631 QUrl QtWebEngineWebWidget::getUrl() const
1632 {
1633 const QUrl url(m_page->url());
1634
1635 return (Utils::isUrlEmpty(url) ? ((url.toString() == QLatin1String("about:blank#blocked")) ? QUrl(QLatin1String("about:start")) : m_page->requestedUrl()) : url);
1636 }
1637
getIcon() const1638 QIcon QtWebEngineWebWidget::getIcon() const
1639 {
1640 if (isPrivate())
1641 {
1642 return ThemesManager::createIcon(QLatin1String("tab-private"));
1643 }
1644
1645 const QIcon icon(m_page->icon());
1646
1647 return (icon.isNull() ? ThemesManager::createIcon(QLatin1String("tab")) : icon);
1648 }
1649
getLastUrlClickTime() const1650 QDateTime QtWebEngineWebWidget::getLastUrlClickTime() const
1651 {
1652 return m_lastUrlClickTime;
1653 }
1654
1655 #if QTWEBENGINECORE_VERSION >= 0x050D00
getBlockedElements() const1656 QStringList QtWebEngineWebWidget::getBlockedElements() const
1657 {
1658 return m_requestInterceptor->getBlockedElements();
1659 }
1660 #endif
1661
getScrollPosition() const1662 QPoint QtWebEngineWebWidget::getScrollPosition() const
1663 {
1664 return m_page->scrollPosition().toPoint();
1665 }
1666
getActiveFrame() const1667 WebWidget::LinkUrl QtWebEngineWebWidget::getActiveFrame() const
1668 {
1669 LinkUrl link;
1670
1671 if (!m_hitResult.frameUrl.isEmpty())
1672 {
1673 link.title = m_hitResult.title;
1674 link.url = m_hitResult.frameUrl;
1675 }
1676
1677 return link;
1678 }
1679
getActiveImage() const1680 WebWidget::LinkUrl QtWebEngineWebWidget::getActiveImage() const
1681 {
1682 LinkUrl link;
1683
1684 if (!m_hitResult.imageUrl.isEmpty())
1685 {
1686 link.title = m_hitResult.alternateText;
1687 link.url = m_hitResult.imageUrl;
1688 }
1689
1690 return link;
1691 }
1692
getActiveLink() const1693 WebWidget::LinkUrl QtWebEngineWebWidget::getActiveLink() const
1694 {
1695 LinkUrl link;
1696
1697 if (!m_hitResult.linkUrl.isEmpty())
1698 {
1699 //TODO Extract text?
1700 link.title = m_hitResult.title;
1701 link.url = m_hitResult.linkUrl;
1702 }
1703
1704 return link;
1705 }
1706
getActiveMedia() const1707 WebWidget::LinkUrl QtWebEngineWebWidget::getActiveMedia() const
1708 {
1709 LinkUrl link;
1710
1711 if (!m_hitResult.mediaUrl.isEmpty())
1712 {
1713 link.title = m_hitResult.title;
1714 link.url = m_hitResult.mediaUrl;
1715 }
1716
1717 return link;
1718 }
1719
getHistory() const1720 WindowHistoryInformation QtWebEngineWebWidget::getHistory() const
1721 {
1722 return m_page->getHistory();
1723 }
1724
getHitTestResult(const QPoint & position)1725 WebWidget::HitTestResult QtWebEngineWebWidget::getHitTestResult(const QPoint &position)
1726 {
1727 m_hitResult = QtWebEngineHitTestResult(m_page->runScriptFile(QLatin1String("hitTest"), {QString::number(position.x() / m_page->zoomFactor()), QString::number(position.y() / m_page->zoomFactor())}));
1728
1729 if (m_hitResult.flags.testFlag(HitTestResult::IsSelectedTest) && !m_hitResult.linkUrl.isValid() && Utils::isUrl(m_page->selectedText()))
1730 {
1731 m_hitResult.flags |= HitTestResult::IsLinkFromSelectionTest;
1732 m_hitResult.linkUrl = QUrl::fromUserInput(m_page->selectedText());
1733 }
1734
1735 return m_hitResult;
1736 }
1737
getStyleSheets() const1738 QStringList QtWebEngineWebWidget::getStyleSheets() const
1739 {
1740 return m_styleSheets;
1741 }
1742
processLinks(const QVariantList & rawLinks) const1743 QVector<WebWidget::LinkUrl> QtWebEngineWebWidget::processLinks(const QVariantList &rawLinks) const
1744 {
1745 QVector<LinkUrl> links;
1746 links.reserve(rawLinks.count());
1747
1748 for (int i = 0; i < rawLinks.count(); ++i)
1749 {
1750 const QVariantHash rawLink(rawLinks.at(i).toHash());
1751 LinkUrl link;
1752 link.title = rawLink.value(QLatin1String("title")).toString();
1753 link.mimeType = rawLink.value(QLatin1String("mimeType")).toString();
1754 link.url = QUrl(rawLink.value(QLatin1String("url")).toString());
1755
1756 links.append(link);
1757 }
1758
1759 return links;
1760 }
1761
getFeeds() const1762 QVector<WebWidget::LinkUrl> QtWebEngineWebWidget::getFeeds() const
1763 {
1764 return m_feeds;
1765 }
1766
getLinks() const1767 QVector<WebWidget::LinkUrl> QtWebEngineWebWidget::getLinks() const
1768 {
1769 return m_links;
1770 }
1771
getSearchEngines() const1772 QVector<WebWidget::LinkUrl> QtWebEngineWebWidget::getSearchEngines() const
1773 {
1774 return m_searchEngines;
1775 }
1776
1777 #if QTWEBENGINECORE_VERSION >= 0x050D00
getBlockedRequests() const1778 QVector<NetworkManager::ResourceInformation> QtWebEngineWebWidget::getBlockedRequests() const
1779 {
1780 return m_requestInterceptor->getBlockedRequests();
1781 }
1782 #endif
1783
getMetaData() const1784 QMultiMap<QString, QString> QtWebEngineWebWidget::getMetaData() const
1785 {
1786 return m_metaData;
1787 }
1788
getLoadingState() const1789 WebWidget::LoadingState QtWebEngineWebWidget::getLoadingState() const
1790 {
1791 return m_loadingState;
1792 }
1793
getZoom() const1794 int QtWebEngineWebWidget::getZoom() const
1795 {
1796 return static_cast<int>(m_page->zoomFactor() * 100);
1797 }
1798
findInPage(const QString & text,FindFlags flags)1799 int QtWebEngineWebWidget::findInPage(const QString &text, FindFlags flags)
1800 {
1801 if (text.isEmpty())
1802 {
1803 m_page->findText(text);
1804
1805 return 0;
1806 }
1807
1808 QWebEnginePage::FindFlags nativeFlags(0);
1809
1810 if (flags.testFlag(BackwardFind))
1811 {
1812 nativeFlags |= QWebEnginePage::FindBackward;
1813 }
1814
1815 if (flags.testFlag(CaseSensitiveFind))
1816 {
1817 nativeFlags |= QWebEnginePage::FindCaseSensitively;
1818 }
1819
1820 QEventLoop eventLoop;
1821 bool hasMatch(false);
1822
1823 m_page->findText(text, nativeFlags, [&](const QVariant &result)
1824 {
1825 hasMatch = result.toBool();
1826
1827 eventLoop.quit();
1828 });
1829
1830 connect(this, &QtWebEngineWebWidget::aboutToReload, &eventLoop, &QEventLoop::quit);
1831 connect(this, &QtWebEngineWebWidget::destroyed, &eventLoop, &QEventLoop::quit);
1832
1833 eventLoop.exec();
1834
1835 return (hasMatch ? -1 : 0);
1836 }
1837
canGoBack() const1838 bool QtWebEngineWebWidget::canGoBack() const
1839 {
1840 return m_page->history()->canGoBack();
1841 }
1842
canGoForward() const1843 bool QtWebEngineWebWidget::canGoForward() const
1844 {
1845 return m_page->history()->canGoForward();
1846 }
1847
canFastForward() const1848 bool QtWebEngineWebWidget::canFastForward() const
1849 {
1850 return (m_canGoForwardValue == TrueValue || canGoForward());
1851 }
1852
1853 #if QTWEBENGINECORE_VERSION >= 0x050B00
canInspect() const1854 bool QtWebEngineWebWidget::canInspect() const
1855 {
1856 return !Utils::isUrlEmpty(getUrl());
1857 }
1858 #endif
1859
canRedo() const1860 bool QtWebEngineWebWidget::canRedo() const
1861 {
1862 return m_page->action(QWebEnginePage::Redo)->isEnabled();
1863 }
1864
canUndo() const1865 bool QtWebEngineWebWidget::canUndo() const
1866 {
1867 return m_page->action(QWebEnginePage::Undo)->isEnabled();
1868 }
1869
canShowContextMenu(const QPoint & position) const1870 bool QtWebEngineWebWidget::canShowContextMenu(const QPoint &position) const
1871 {
1872 Q_UNUSED(position)
1873
1874 return true;
1875 }
1876
canViewSource() const1877 bool QtWebEngineWebWidget::canViewSource() const
1878 {
1879 return (!m_page->isViewingMedia() && !Utils::isUrlEmpty(getUrl()));
1880 }
1881
hasSelection() const1882 bool QtWebEngineWebWidget::hasSelection() const
1883 {
1884 return (m_page->hasSelection() && !m_page->selectedText().isEmpty());
1885 }
1886
hasWatchedChanges(WebWidget::ChangeWatcher watcher) const1887 bool QtWebEngineWebWidget::hasWatchedChanges(WebWidget::ChangeWatcher watcher) const
1888 {
1889 return m_watchedChanges.value(watcher, false);
1890 }
1891
isAudible() const1892 bool QtWebEngineWebWidget::isAudible() const
1893 {
1894 return m_page->recentlyAudible();
1895 }
1896
isAudioMuted() const1897 bool QtWebEngineWebWidget::isAudioMuted() const
1898 {
1899 return m_page->isAudioMuted();
1900 }
1901
isFullScreen() const1902 bool QtWebEngineWebWidget::isFullScreen() const
1903 {
1904 return m_isFullScreen;
1905 }
1906
1907 #if QTWEBENGINECORE_VERSION >= 0x050B00
isInspecting() const1908 bool QtWebEngineWebWidget::isInspecting() const
1909 {
1910 return (m_inspectorWidget && m_inspectorWidget->isVisible());
1911 }
1912 #endif
1913
isPopup() const1914 bool QtWebEngineWebWidget::isPopup() const
1915 {
1916 return m_page->isPopup();
1917 }
1918
isPrivate() const1919 bool QtWebEngineWebWidget::isPrivate() const
1920 {
1921 return m_page->profile()->isOffTheRecord();
1922 }
1923
isScrollBar(const QPoint & position) const1924 bool QtWebEngineWebWidget::isScrollBar(const QPoint &position) const
1925 {
1926 Q_UNUSED(position)
1927
1928 return false;
1929 }
1930
isTypedIn() const1931 bool QtWebEngineWebWidget::isTypedIn() const
1932 {
1933 return m_isTypedIn;
1934 }
1935
eventFilter(QObject * object,QEvent * event)1936 bool QtWebEngineWebWidget::eventFilter(QObject *object, QEvent *event)
1937 {
1938 switch (event->type())
1939 {
1940 case QEvent::ChildAdded:
1941 if (object == m_webView)
1942 {
1943 const QChildEvent *childEvent(static_cast<QChildEvent*>(event));
1944
1945 if (childEvent->child())
1946 {
1947 childEvent->child()->installEventFilter(this);
1948 }
1949 }
1950
1951 break;
1952 case QEvent::ContextMenu:
1953 if (object == m_webView)
1954 {
1955 const QContextMenuEvent *contextMenuEvent(static_cast<QContextMenuEvent*>(event));
1956
1957 if (contextMenuEvent && contextMenuEvent->reason() != QContextMenuEvent::Mouse)
1958 {
1959 triggerAction(ActionsManager::ContextMenuAction, {{QLatin1String("context"), contextMenuEvent->reason()}});
1960 }
1961 }
1962
1963 break;
1964 case QEvent::MouseButtonDblClick:
1965 case QEvent::MouseButtonPress:
1966 case QEvent::Wheel:
1967 {
1968 const QMouseEvent *mouseEvent(static_cast<QMouseEvent*>(event));
1969
1970 if (mouseEvent)
1971 {
1972 setClickPosition(mouseEvent->pos());
1973 updateHitTestResult(mouseEvent->pos());
1974
1975 if (mouseEvent->button() == Qt::LeftButton && !getCurrentHitTestResult().linkUrl.isEmpty())
1976 {
1977 m_lastUrlClickTime = QDateTime::currentDateTime();
1978 }
1979 }
1980
1981 QVector<GesturesManager::GesturesContext> contexts;
1982
1983 if (getCurrentHitTestResult().flags.testFlag(HitTestResult::IsContentEditableTest))
1984 {
1985 contexts.append(GesturesManager::ContentEditableContext);
1986 }
1987
1988 if (getCurrentHitTestResult().linkUrl.isValid())
1989 {
1990 contexts.append(GesturesManager::LinkContext);
1991 }
1992
1993 contexts.append(GesturesManager::GenericContext);
1994
1995 if ((!mouseEvent || !isScrollBar(mouseEvent->pos())) && GesturesManager::startGesture(object, event, contexts))
1996 {
1997 return true;
1998 }
1999
2000 if (event->type() == QEvent::MouseButtonDblClick && mouseEvent->button() == Qt::LeftButton && SettingsManager::getOption(SettingsManager::Browser_ShowSelectionContextMenuOnDoubleClickOption).toBool())
2001 {
2002 const HitTestResult hitResult(getHitTestResult(mouseEvent->pos()));
2003
2004 if (!hitResult.flags.testFlag(HitTestResult::IsContentEditableTest) && hitResult.tagName != QLatin1String("textarea") && hitResult.tagName!= QLatin1String("select") && hitResult.tagName != QLatin1String("input"))
2005 {
2006 setClickPosition(mouseEvent->pos());
2007
2008 QTimer::singleShot(250, this, [&]()
2009 {
2010 showContextMenu();
2011 });
2012 }
2013 }
2014 }
2015
2016 break;
2017 case QEvent::Move:
2018 case QEvent::Resize:
2019 if (object == m_webView)
2020 {
2021 emit geometryChanged();
2022 }
2023
2024 break;
2025 case QEvent::ShortcutOverride:
2026 m_isEditing = m_page->runScriptSource(QLatin1String("var element = document.body.querySelector(':focus'); var tagName = (element ? element.tagName.toLowerCase() : ''); var result = false; if (tagName == 'textarea' || tagName == 'input') { var type = (element.type ? element.type.toLowerCase() : ''); if ((type == '' || tagName == 'textarea' || type == 'text' || type == 'search') && !element.hasAttribute('readonly') && !element.hasAttribute('disabled')) { result = true; } } result;")).toBool();
2027
2028 if (m_isEditing)
2029 {
2030 event->accept();
2031
2032 return true;
2033 }
2034
2035 break;
2036 case QEvent::ToolTip:
2037 handleToolTipEvent(static_cast<QHelpEvent*>(event), m_webView);
2038
2039 return true;
2040 default:
2041 break;
2042 }
2043
2044 return QObject::eventFilter(object, event);
2045 }
2046
2047 }
2048