玉兔远程控制 0.1.0-bate8
载入中...
搜索中...
未找到
FrmExtensionManager.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include <QClipboard>
4#include <QApplication>
5#include <QLoggingCategory>
6#include <QVBoxLayout>
7#include <QHBoxLayout>
8#include <QHeaderView>
9#include <QPushButton>
10#include <QTableView>
11#include <QMessageBox>
12#include <QFileDialog>
13#include <QStandardItemModel>
14#include <QWebEngineProfile>
15#include <QJsonDocument>
16#include <QJsonObject>
17#include <QJsonArray>
18#include <QFile>
19#include <QDir>
20#include <QSettings>
21#include <QCryptographicHash>
22#include <QDesktopServices>
23#include <QUrl>
24#include <QMenu>
25#include <QToolBar>
26
27#include "FrmExtensionManager.h"
28#include "ParameterWebBrowser.h"
29#include "ui_FrmExtensionManager.h"
30
31static Q_LOGGING_CATEGORY(log, "WebBrowser.ExtensionManager")
32
34 QWebEngineProfile *profile, QWidget *parent)
35 : QWidget(parent)
36 , ui(new Ui::CFrmExtensionManager)
37 , m_pProfile(nullptr)
38 , m_pModelExtensions(nullptr)
39 , m_pInstall(nullptr)
40 , m_pLoad(nullptr)
41 , m_pUninstall(nullptr)
42 , m_pRefresh(nullptr)
43 , m_pCopyUrl(nullptr)
44 , m_pCopyPath(nullptr)
45 , m_pOpenFolder(nullptr)
46{
47 qDebug(log) << Q_FUNC_INFO;
48
49 ui->setupUi(this);
50 setWindowTitle(tr("Chrome Extension Manager"));
51 setWindowIcon(QIcon::fromTheme("extension"));
52
53 InitializeUI();
54 SetupConnections();
55
56 SetProfile(profile);
57}
58
59CFrmExtensionManager::~CFrmExtensionManager()
60{
61 qDebug(log) << Q_FUNC_INFO;
62 delete ui;
63}
64
65void CFrmExtensionManager::InitializeUI()
66{
67 QToolBar* pToolBar = ui->toolBar;
68 m_pInstall = pToolBar->addAction(
69 QIcon::fromTheme("system-software-install"), tr("Install Extension"),
70 this, &CFrmExtensionManager::on_pbInstall_clicked);
71 m_pLoad = pToolBar->addAction(
72 QIcon::fromTheme("system-software-update"), tr("Load Extension"),
73 this, &CFrmExtensionManager::on_pbLoad_clicked);
74 m_pUninstall = pToolBar->addAction(
75 QIcon::fromTheme("edit-delete"), tr("Uninstall"),
76 this, &CFrmExtensionManager::on_pbUninstall_clicked);
77 m_pRefresh = pToolBar->addAction(QIcon::fromTheme("view-refresh"), tr("Refresh"),
78 this, &CFrmExtensionManager::on_pbRefresh_clicked);
79 m_pCopyUrl = pToolBar->addAction(QIcon::fromTheme("edit-copy"), tr("Copy popup url to clipboard"),
80 this, &CFrmExtensionManager::slotCopyPopupUrl);
81 m_pCopyPath = pToolBar->addAction(QIcon::fromTheme("edit-copy"), tr("Copy path to clipboard"),
82 this, &CFrmExtensionManager::slotCopyPath);
83 m_pDetails = pToolBar->addAction(QIcon::fromTheme("dialog-information"), tr("Details"),
84 this, &CFrmExtensionManager::on_pbDetails_clicked);
85 m_pOpenFolder = pToolBar->addAction(QIcon::fromTheme("folder-open"), tr("Open Folder"),
86 this, &CFrmExtensionManager::on_pbOpenExtensionPath_clicked);
87
88 // 创建扩展列表模型
89 m_pModelExtensions = new QStandardItemModel(this);
90
91 // 设置表头
92 QStringList headers;
93 headers << tr("Name") << tr("Enabled") << tr("Description")
94 << tr("Popup url") << tr("ID") << tr("Path");
95 m_pModelExtensions->setHorizontalHeaderLabels(headers);
96
97 // 配置表视图
98 ui->tvExtensions->setModel(m_pModelExtensions);
99 ui->tvExtensions->setSelectionMode(QAbstractItemView::SingleSelection);
100 ui->tvExtensions->setSelectionBehavior(QAbstractItemView::SelectRows);
101 ui->tvExtensions->setEditTriggers(QAbstractItemView::NoEditTriggers);
102 ui->tvExtensions->setContextMenuPolicy(Qt::CustomContextMenu);
103 ui->tvExtensions->setAlternatingRowColors(true);
104
105 // 设置列宽自适应
106 ui->tvExtensions->horizontalHeader()->setStretchLastSection(true);
107 ui->tvExtensions->verticalHeader()->setDefaultSectionSize(24);
108
109 slotSelectionChanged();
110 qDebug(log) << "UI initialized";
111}
112
113void CFrmExtensionManager::SetupConnections()
114{
115 bool check = true;
116 // 模型和表视图连接
117 check = connect(m_pModelExtensions, &QStandardItemModel::itemChanged,
118 this, &CFrmExtensionManager::slotExtensionItemChanged);
119 Q_ASSERT(check);
120 check = connect(ui->tvExtensions->selectionModel(), &QItemSelectionModel::selectionChanged,
121 this, &CFrmExtensionManager::slotSelectionChanged);
122 Q_ASSERT(check);
123 check = connect(ui->tvExtensions, &QTableView::customContextMenuRequested,
124 this, &CFrmExtensionManager::slotCustomContextMenu);
125 Q_ASSERT(check);
126}
127
128int CFrmExtensionManager::SetProfile(QWebEngineProfile *profile)
129{
130 m_pProfile = profile;
131 if(!m_pProfile) {
132 qWarning(log) << "Failed to set profile: profile is null";
133 return -1;
134 }
135
136 bool check = false;
137 auto m = m_pProfile->extensionManager();
138 m->disconnect(this);
139 check = connect(m, &QWebEngineExtensionManager::installFinished,
140 this, &CFrmExtensionManager::slotInstallFinished);
141 Q_ASSERT(check);
142 check = connect(m, &QWebEngineExtensionManager::uninstallFinished,
143 this, &CFrmExtensionManager::slotUninstallFinished);
144 Q_ASSERT(check);
145 check = connect(m, &QWebEngineExtensionManager::loadFinished,
146 this, &CFrmExtensionManager::slotLoadFinished);
147 Q_ASSERT(check);
148 check = connect(m, &QWebEngineExtensionManager::unloadFinished,
149 this, &CFrmExtensionManager::slotUnloadFinished);
150 Q_ASSERT(check);
151
152 RefreshExtensionList();
153
154 return 0;
155}
156
158{
159 //qDebug(log) << Q_FUNC_INFO << "Path:" << path;
160
161 if(!m_pProfile || !m_pProfile->extensionManager()) {
162 qWarning(log) << "Profile is not set";
163 return;
164 }
165
166 QFileInfo fi(path);
167 if(!fi.exists()) {
168 qCritical(log) << "The path is not exist" << path;
169 return;
170 }
171
172 auto m = m_pProfile->extensionManager();
173 if(fi.isFile())
174 m->installExtension(path);
175 else if(fi.isDir()){
176 m->loadExtension(path);
177 }
178}
179
181{
182 if(!m_pProfile || !m_pProfile->extensionManager()) {
183 qWarning(log) << "Profile is not set";
184 return;
185 }
186
187 auto m = m_pProfile->extensionManager();
188 foreach (auto e, m->extensions()) {
189 if(e.id() == id)
190 m->uninstallExtension(e);
191 }
192
193 //qDebug(log) << "Extension uninstalled:" << id;
194}
195
196void CFrmExtensionManager::EnableExtension(const QString &id, bool bEnable)
197{
198 qDebug(log) << Q_FUNC_INFO << "ID:" << id;
199 if(id.isEmpty()) return;
200 if(!m_pProfile || !m_pProfile->extensionManager()) {
201 qWarning(log) << "Profile is not set";
202 return;
203 }
204
205 auto m = m_pProfile->extensionManager();
206 foreach (auto e, m->extensions()) {
207 if(e.id() == id && e.isEnabled() != bEnable)
208 m->setExtensionEnabled(e, bEnable);
209 }
210}
211
212QList<QWebEngineExtensionInfo> CFrmExtensionManager::GetInstalledExtensions() const
213{
214 if(!m_pProfile || !m_pProfile->extensionManager()) {
215 qWarning(log) << "Profile is not set";
216 return QList<QWebEngineExtensionInfo>();
217 }
218
219 auto m = m_pProfile->extensionManager();
220 return m->extensions();
221}
222
223QWebEngineExtensionInfo CFrmExtensionManager::GetExtensionInfo(const QString &id) const
224{
225 if(!m_pProfile || !m_pProfile->extensionManager()) {
226 qWarning(log) << "Profile is not set";
227 return QWebEngineExtensionInfo();
228 }
229
230 auto m = m_pProfile->extensionManager();
231 foreach(auto e, m->extensions()) {
232 if(e.id() == id)
233 return e;
234 }
235 return QWebEngineExtensionInfo();
236}
237
238int CFrmExtensionManager::RefreshExtensionList()
239{
240 if(!m_pModelExtensions || !m_pProfile || !m_pProfile->extensionManager())
241 return -1;
242
243 m_pModelExtensions->removeRows(0, m_pModelExtensions->rowCount());
244
245 qDebug(log) << "Refreshing extension list";
246
247 auto extensions = m_pProfile->extensionManager()->extensions();
248 foreach(auto e, extensions) {
249 AddExtensionItem(e);
250 }
251
252 qDebug(log) << "Extension list refreshed, total:" << m_pModelExtensions->rowCount();
253 return 0;
254}
255
256int CFrmExtensionManager::AddExtensionItem(const QWebEngineExtensionInfo &info)
257{
258 if(!m_pModelExtensions) return -1;
259
260 int row = m_pModelExtensions->rowCount();
261 m_pModelExtensions->insertRow(row);
262
263 auto itemId = new QStandardItem(info.id());
264 itemId->setEditable(false);
265 m_pModelExtensions->setItem(row, ColumnNo::ID, itemId);
266
267 auto itemName = new QStandardItem(info.name());
268 itemName->setEditable(false);
269 m_pModelExtensions->setItem(row, ColumnNo::Name, itemName);
270
271 auto itemEnabled = new QStandardItem();
272 itemEnabled->setEditable(false);
273 itemEnabled->setCheckable(true);
274 itemEnabled->setCheckState(info.isEnabled() ? Qt::Checked : Qt::Unchecked);
275 m_pModelExtensions->setItem(row, ColumnNo::Enabled, itemEnabled);
276
277 auto itemPath = new QStandardItem(info.path());
278 itemPath->setEditable(false);
279 m_pModelExtensions->setItem(row, ColumnNo::Path, itemPath);
280
281 auto itemPopupUrl = new QStandardItem(info.actionPopupUrl().toString());
282 itemPopupUrl->setEditable(false);
283 m_pModelExtensions->setItem(row, ColumnNo::PopupUrl, itemPopupUrl);
284
285 auto itemDesc = new QStandardItem(info.description());
286 itemDesc->setEditable(false);
287 m_pModelExtensions->setItem(row, ColumnNo::Description, itemDesc);
288
289 qDebug(log) << "Added extension:" << info.name();
290 return 0;
291}
292
293int CFrmExtensionManager::RemoveExtensionItem(const QString &extensionId)
294{
295 for(int i = 0; i < m_pModelExtensions->rowCount(); i++) {
296 auto item = m_pModelExtensions->item(i, ColumnNo::ID);
297 if(item && item->text() == extensionId) {
298 m_pModelExtensions->removeRow(i);
299 return 0;
300 }
301 }
302 return -1;
303}
304
305void CFrmExtensionManager::on_pbInstall_clicked()
306{
307 QString filter = tr("Chrome Extension (*.crx);;All Files (*)");
308 QString path = QFileDialog::getOpenFileName(
309 this, tr("Install Chrome Extension"), "", filter);
310
311 if(!path.isEmpty()) {
312 InstallExtension(path);
313 }
314}
315
316void CFrmExtensionManager::on_pbLoad_clicked()
317{
318 QString path = QFileDialog::getExistingDirectory(
319 this, tr("Load Chrome Extension"));
320 if(!path.isEmpty()) {
321 InstallExtension(path);
322 }
323}
324
325void CFrmExtensionManager::on_pbUninstall_clicked()
326{
327 qDebug(log) << Q_FUNC_INFO;
328
329 auto indexes = ui->tvExtensions->selectionModel()->selectedRows();
330 if(indexes.isEmpty()) {
331 QMessageBox::warning(this, tr("Warning"),
332 tr("Please select an extension to uninstall"));
333 return;
334 }
335
336 int row = indexes.at(0).row();
337 auto idItem = m_pModelExtensions->item(row, ColumnNo::ID);
338 auto nameItem = m_pModelExtensions->item(row, ColumnNo::Name);
339
340 if(!idItem || !nameItem) return;
341
342 int ret = QMessageBox::question(this, tr("Confirm"),
343 tr("Do you want to uninstall '%1'?").arg(nameItem->text()),
344 QMessageBox::Yes | QMessageBox::No);
345
346 if(ret == QMessageBox::Yes) {
347 UninstallExtension(idItem->text());
348 }
349}
350
351void CFrmExtensionManager::on_pbRefresh_clicked()
352{
353 qDebug(log) << Q_FUNC_INFO;
354 RefreshExtensionList();
355}
356
357void CFrmExtensionManager::on_pbDetails_clicked()
358{
359 qDebug(log) << Q_FUNC_INFO;
360
361 auto indexes = ui->tvExtensions->selectionModel()->selectedRows();
362 if(indexes.isEmpty()) return;
363
364 int row = indexes.at(0).row();
365 auto idItem = m_pModelExtensions->item(row, ColumnNo::ID);
366 if(!idItem) return;
367
368 auto info = GetExtensionInfo(idItem->text());
369
370 QString details = tr("Extension Details:") + "\n";
371 details += " - " + tr("Name:") + " " + info.name() + "\n";
372 details += " - " + tr("Description:") + " " + info.description() + "\n";
373 details += " - " + tr("Enable:") + " " + (info.isEnabled() ? tr("TRUE") : tr("FALSE")) + "\n";
374 details += " - " + tr("ID:") + " " + info.id() + "\n";
375 details += " - " + tr("Popup url:") + " " + info.actionPopupUrl().toString() + "\n";
376 details += " - " + tr("Path:") + " " + info.path() + "\n";
377
378 QMessageBox::information(this, tr("Extension Details"), details);
379}
380
381void CFrmExtensionManager::on_pbOpenDevTools_clicked()
382{
383 qDebug(log) << Q_FUNC_INFO;
384 // TODO: 打开扩展开发者工具
385 QMessageBox::information(this, tr("Info"),
386 tr("Developer tools for extensions not yet implemented"));
387}
388
389void CFrmExtensionManager::on_pbOpenExtensionPath_clicked()
390{
391 qDebug(log) << Q_FUNC_INFO;
392
393 auto indexes = ui->tvExtensions->selectionModel()->selectedRows();
394 if(indexes.isEmpty()) return;
395
396 int row = indexes.at(0).row();
397 auto pathItem = m_pModelExtensions->item(row, ColumnNo::Path);
398 if(pathItem && !pathItem->text().isEmpty()) {
399 QDesktopServices::openUrl(QUrl::fromLocalFile(pathItem->text()));
400 }
401}
402
403void CFrmExtensionManager::slotExtensionItemChanged(QStandardItem *item)
404{
405 if(!item) return;
406 //qDebug(log) << "Extension item changed:" << item->text();
407 if(!item->index().isValid()) return;
408 if(item->index().column() == ColumnNo::Enabled) {
409 auto idItem = m_pModelExtensions->item(item->index().row(), ColumnNo::ID);
410 if(idItem) {
411 EnableExtension(idItem->text(), item->checkState() == Qt::Checked);
412 }
413 }
414}
415
416void CFrmExtensionManager::slotSelectionChanged()
417{
418 auto indexes = ui->tvExtensions->selectionModel()->selectedRows();
419 bool hasSelection = !indexes.isEmpty();
420
421 m_pUninstall->setEnabled(hasSelection);
422 m_pCopyUrl->setEnabled(hasSelection);
423 m_pDetails->setEnabled(hasSelection);
424 m_pOpenFolder->setEnabled(hasSelection);
425}
426
427void CFrmExtensionManager::slotCustomContextMenu(const QPoint &pos)
428{
429 qDebug(log) << Q_FUNC_INFO;
430 QMenu m(this);
431 auto indexes = ui->tvExtensions->selectionModel()->selectedRows();
432 if(!indexes.isEmpty()) {
433 m.addAction(m_pCopyUrl);
434 m.addAction(m_pCopyPath);
435 m.addAction(m_pDetails);
436 m.addAction(m_pOpenFolder);
437 m.addSeparator();
438 m.addAction(m_pUninstall);
439 }
440 m.addAction(m_pInstall);
441 m.addAction(m_pLoad);
442 m.addAction(m_pRefresh);
443 auto pw = qobject_cast<QWidget*>(sender());
444 if(pw)
445 m.exec(pw->mapToGlobal(pos));
446}
447
448void CFrmExtensionManager::slotInstallFinished(const QWebEngineExtensionInfo &extension)
449{
450 if(extension.isInstalled()) {
451 QMessageBox::information(
452 this, tr("Successful"),
453 tr("Successfully installed extension") + " \"" + extension.name() + "\"\n\n"
454 + tr("Path:") + " " + extension.path());
455 AddExtensionItem(extension);
456 } else
457 QMessageBox::critical(
458 this, tr("Failed"),
459 tr("Failed to install extension.") + "\n\n"
460 + tr("Path:") + " " + extension.path() + "\n\n"
461 + tr("Error:") + " " + extension.error());
462}
463
464void CFrmExtensionManager::slotUninstallFinished(const QWebEngineExtensionInfo &extension)
465{
466 if(!extension.isInstalled()) {
467 QMessageBox::information(this, tr("Successful"),
468 tr("Successfully uninstalled extension") + " \"" + extension.name() + "\"");
469 RemoveExtensionItem(extension.id());
470 } else
471 QMessageBox::critical(
472 this, tr("Failed"),
473 tr("Failed to uninstall extension.") + "\n\n"
474 + tr("Path:") + " " + extension.path() + "\n\n"
475 + tr("Error:") + " " + extension.error());
476}
477
478void CFrmExtensionManager::slotLoadFinished(const QWebEngineExtensionInfo &extension)
479{
480 if(extension.isLoaded()) {
481 QMessageBox::information(
482 this, tr("Successful"),
483 tr("Successfully loaded extension") + " \"" + extension.name() + "\"\n\n"
484 + tr("Path:") + " " + extension.path());
485 AddExtensionItem(extension);
486 } else
487 QMessageBox::critical(
488 this, tr("Failed"),
489 tr("Failed to load extension.") + "\n\n"
490 + tr("Path:") + " " + extension.path() + "\n\n"
491 + tr("Error:") + " " + extension.error());
492}
493
494void CFrmExtensionManager::slotUnloadFinished(const QWebEngineExtensionInfo &extension)
495{
496 if(!extension.isLoaded()) {
497 QMessageBox::information(this, tr("Successful"),
498 tr("Successfully unloaded extension") + " \"" + extension.name() + "\"");
499 RemoveExtensionItem(extension.id());
500 } else
501 QMessageBox::critical(
502 this, tr("Failed"),
503 tr("Failed to unload extension.") + "\n\n"
504 + tr("Path:") + " " + extension.path() + "\n\n"
505 + tr("Error:") + " " + extension.error());
506}
507
508void CFrmExtensionManager::slotCopyPopupUrl()
509{
510 if(!m_pModelExtensions) return;
511 auto indexes = ui->tvExtensions->selectionModel()->selectedRows();
512 if(indexes.isEmpty()) return;
513
514 int row = indexes.at(0).row();
515 auto item = m_pModelExtensions->item(row, ColumnNo::PopupUrl);
516
517 QClipboard* clipboard = QApplication::clipboard();
518 if(clipboard && item && !item->text().isEmpty()) {
519 clipboard->setText(item->text());
520 }
521}
522
523void CFrmExtensionManager::slotCopyPath()
524{
525 if(!m_pModelExtensions) return;
526 auto indexes = ui->tvExtensions->selectionModel()->selectedRows();
527 if(indexes.isEmpty()) return;
528
529 int row = indexes.at(0).row();
530 auto item = m_pModelExtensions->item(row, ColumnNo::Path);
531
532 QClipboard* clipboard = QApplication::clipboard();
533 if(clipboard && item && !item->text().isEmpty()) {
534 clipboard->setText(item->text());
535 }
536}
扩展管理器界面
void EnableExtension(const QString &id, bool bEnable)
启用扩展
QWebEngineExtensionInfo GetExtensionInfo(const QString &id) const
获取扩展详细信息
void UninstallExtension(const QString &id)
卸载扩展
QList< QWebEngineExtensionInfo > GetInstalledExtensions() const
获取已安装的扩展列表
void InstallExtension(const QString &path)
安装或加载 Chrome 扩展
int SetProfile(QWebEngineProfile *profile)
设置 WebEngine Profile