improved the extension

This commit is contained in:
fourtf 2018-05-30 17:17:27 +02:00
parent e01a3a0978
commit 3c9c3493ae
8 changed files with 298 additions and 156 deletions

View file

@ -8,6 +8,9 @@ const ignoredPages = {
"directory": true, "directory": true,
}; };
let popup = chrome.extension.getViews({type: "popup"})[0];
popup.window.getElementById("status").innerHTML = "NaM";
/// return channel name if it should contain a chat /// return channel name if it should contain a chat
function matchChannelName(url) { function matchChannelName(url) {
if (!url) if (!url)
@ -43,6 +46,7 @@ function getPort() {
function connectPort() { function connectPort() {
port = chrome.runtime.connectNative(appName); port = chrome.runtime.connectNative(appName);
console.log("port connected"); console.log("port connected");
let connected = true;
port.onMessage.addListener(function (msg) { port.onMessage.addListener(function (msg) {
console.log(msg); console.log(msg);
@ -52,6 +56,14 @@ function connectPort() {
port = null; port = null;
}); });
let sendPing = () => {
if (connected) {
port.postMessage({ ping: true });
} else {
setTimeout(sendPing, 5000);
}
}
} }
@ -63,6 +75,12 @@ chrome.tabs.onActivated.addListener((activeInfo) => {
chrome.windows.get(tab.windowId, {}, (window) => { chrome.windows.get(tab.windowId, {}, (window) => {
if (!window.focused) return; if (!window.focused) return;
if (window.state == "fullscreen") {
tryDetach(tab.windowId);
return;
}
console.log("onActivated");
onTabSelected(tab.url, tab); onTabSelected(tab.url, tab);
}); });
}); });
@ -73,16 +91,27 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (!tab.highlighted) if (!tab.highlighted)
return; return;
onTabSelected(changeInfo.url, tab); chrome.windows.get(tab.windowId, {}, (window) => {
if (!window.focused) return;
if (window.state == "fullscreen") {
tryDetach(tab.windowId);
return;
}
console.log("onUpdated");
onTabSelected(tab.url, tab);
});
}); });
// tab detached // tab detached
chrome.tabs.onDetached.addListener((tabId, detachInfo) => { chrome.tabs.onDetached.addListener((tabId, detachInfo) => {
console.log("onDetached");
tryDetach(detachInfo.oldWindowId); tryDetach(detachInfo.oldWindowId);
}); });
// tab closed // tab closed
chrome.windows.onRemoved.addListener((windowId) => { chrome.windows.onRemoved.addListener((windowId) => {
console.log("onRemoved");
tryDetach(windowId); tryDetach(windowId);
}); });
@ -96,13 +125,20 @@ chrome.windows.onFocusChanged.addListener((windowId) => {
if (tabs.length === 1) { if (tabs.length === 1) {
let tab = tabs[0]; let tab = tabs[0];
chrome.windows.get(tab.windowId, (window) => {
if (window.state == "fullscreen") {
tryDetach(tab.windowId);
return;
}
console.log("onFocusChanged");
onTabSelected(tab.url, tab); onTabSelected(tab.url, tab);
});
} }
}); });
}); });
// attach or detach from tab // attach or detach from tab
function onTabSelected(url, tab) { function onTabSelected(url, tab) {
let channelName = matchChannelName(url); let channelName = matchChannelName(url);
@ -131,6 +167,10 @@ chrome.runtime.onMessage.addListener((message, sender, callback) => {
// is window focused // is window focused
chrome.windows.get(sender.tab.windowId, {}, (window) => { chrome.windows.get(sender.tab.windowId, {}, (window) => {
if (!window.focused) return; if (!window.focused) return;
if (window.state == "fullscreen") {
tryDetach(sender.tab.windowId);
return;
}
// get zoom value // get zoom value
chrome.tabs.getZoom(sender.tab.id, (zoom) => { chrome.tabs.getZoom(sender.tab.id, (zoom) => {
@ -169,6 +209,8 @@ function tryAttach(windowId, data) {
function tryDetach(windowId) { function tryDetach(windowId) {
let port = getPort(); let port = getPort();
console.log("tryDetach");
if (port) { if (port) {
port.postMessage({ port.postMessage({
action: "detach", action: "detach",

View file

@ -2,72 +2,9 @@
let lastRect = {}; let lastRect = {};
let port = null; let port = null;
function log(str) { let installedObjects = {};
console.log("Chatterino Native: " + str); let rightCollapseButton = null;
} let isCollapsed = false;
function findChatDiv() {
return document.getElementsByClassName("right-column")[0];
}
function queryChatRect() {
if (!matchChannelName(window.location.href)) return;
let element = findChatDiv();
if (element === undefined) {
log("failed to find chat div");
return;
}
if (!element.chatterino) {
let xd = element.getElementsByClassName("channel-page__right-column")[0]
if (xd != undefined) {
element.chatterino = true;
xd.style.opacity = 0;
setTimeout(() => {
xd.innerHTML = "<div style='width: 340px; height: 100%; justify-content: center; display: flex; flex-direction: column; text-align: center; color: #999; user-select: none;'>" +
"Disconnected from the chatterino extension.<br><br>Please refresh the page." +
"</div>";
xd.style.opacity = 1;
}, 2000);
}
}
let rect = element.getBoundingClientRect();
// if (
// lastRect.left == rect.left &&
// lastRect.right == rect.right &&
// lastRect.top == rect.top &&
// lastRect.bottom == rect.bottom
// ) {
// // log("skipped sending message");
// return;
// }
lastRect = rect;
let data = {
rect: rect,
};
try {
chrome.runtime.sendMessage(data);
} catch {
// failed to send a message to the runtime -> maybe the extension got reloaded
// alert("reload the page to re-enable chatterino native");
}
}
function queryCharRectLoop() {
let t1 = performance.now();
queryChatRect();
let t2 = performance.now();
console.log("queryCharRect " + (t2 - t1) + "ms");
// setTimeout(queryCharRectLoop, 500);
}
const ignoredPages = { const ignoredPages = {
"settings": true, "settings": true,
@ -79,7 +16,126 @@
"directory": true, "directory": true,
}; };
/// return channel name if it should contain a chat let findChatDiv = () => document.getElementsByClassName("right-column")[0];
let findRightCollapse = () => document.getElementsByClassName("right-column__toggle-visibility")[0];
let findRightColumn = () => document.getElementsByClassName("channel-page__right-column")[0];
let findNavBar = () => document.getElementsByClassName("top-nav__menu")[0];
// logging function
function log(str) {
console.log("Chatterino Native: " + str);
}
// install events
function installChatterino() {
log("trying to install events");
let retry = false;
// right collapse button
if (!installedObjects.rightCollapse) {
retry = true;
let x = findRightCollapse();
if (x != undefined) {
rightCollapseButton = x;
x.addEventListener("mouseup", () => {
let y = findChatDiv();
if (parseInt(y.style.width) == 0) {
y.style.width = "340px";
isCollapsed = false;
} else {
y.style.width = 0;
isCollapsed = true;
}
});
installedObjects.rightCollapse = true;
}
}
// right column
if (!installedObjects.rightColumn && installedObjects.rightCollapse) {
let x = findChatDiv();
if (x != undefined && x.children.length >= 2) {
x.children[0].innerHTML = "<div style='width: 340px; height: 100%; justify-content: center; display: flex; flex-direction: column; text-align: center; color: #999; user-select: none; background: #222;'>" +
"Disconnected from the chatterino extension.<br><br>Please focus the window or refresh the page." +
"</div>";
installedObjects.rightColumn = true;
} else {
retry = true;
}
}
// nav bar
if (rightCollapseButton && !installedObjects.topNav) {
let x = findRightCollapse();
installedObjects.topNav = true;
} else {
retry = true;
}
// retry if needed
if (retry) {
setTimeout(installChatterino, 1000);
} else {
log("installed all events");
}
}
// query the rect of the chat
function queryChatRect() {
if (!matchChannelName(window.location.href)) return;
let element = findChatDiv();
if (element === undefined) {
log("failed to find chat div");
return;
}
let rect = element.getBoundingClientRect();
/* if (
lastRect.left == rect.left &&
lastRect.right == rect.right &&
lastRect.top == rect.top &&
lastRect.bottom == rect.bottom
) {
// log("skipped sending message");
return;
} */
lastRect = rect;
let data = {
rect: rect,
};
isCollapsed = rect.width == 0;
try {
chrome.runtime.sendMessage(data);
} catch {
// failed to send a message to the runtime -> maybe the extension got reloaded
// alert("reload the page to re-enable chatterino native");
}
}
function queryChatRectLoop() {
let t1 = performance.now();
queryChatRect();
let t2 = performance.now();
console.log("queryCharRect " + (t2 - t1) + "ms");
// setTimeout(queryCharRectLoop, 500);
}
// return channel name if it should contain a chat or undefined
function matchChannelName(url) { function matchChannelName(url) {
if (!url) if (!url)
return undefined; return undefined;
@ -94,15 +150,20 @@
return undefined; return undefined;
} }
queryCharRectLoop();
window.addEventListener("load", () => { // event listeners
setTimeout(queryChatRect, 1000); window.addEventListener("load", () => setTimeout(queryChatRect, 1000));
});
window.addEventListener("resize", queryChatRect); window.addEventListener("resize", queryChatRect);
window.addEventListener("focus", queryChatRect); window.addEventListener("focus", queryChatRect);
window.addEventListener("mouseup", () => { window.addEventListener("mouseup", () => setTimeout(queryChatRect, 10));
setTimeout(queryChatRect, 10); window.addEventListener("hashchange", () => {
installedObjects = {};
installChatterino();
}); });
log("initialized"); //
log("hello there in the dev tools 👋");
queryChatRectLoop();
installChatterino();
})() })()

View file

@ -1,6 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<body> <body>
xd <div>
Chatterino extension :)
</div>
</body> </body>
</html> </html>

@ -1 +1 @@
Subproject commit 439b585976a864de679fc5d5b2c688380b2ef72c Subproject commit ad31b38866d80a17ced902476ed06da69edce3a0

View file

@ -84,10 +84,10 @@ int runGui(int argc, char *argv[])
qApp->setPalette(darkPalette); qApp->setPalette(darkPalette);
// Install native event handler for hidpi on windows // Install native event handler for hidpi on windows
#ifdef USEWINSDK //#ifdef USEWINSDK
a.installNativeEventFilter(new chatterino::util::DpiNativeEventFilter); // a.installNativeEventFilter(new chatterino::util::DpiNativeEventFilter);
#endif //#endif
// Initialize NetworkManager // Initialize NetworkManager
chatterino::util::NetworkManager::init(); chatterino::util::NetworkManager::init();
@ -149,6 +149,17 @@ void runNativeMessagingHost()
bool bigEndian = isBigEndian(); bool bigEndian = isBigEndian();
#endif #endif
std::atomic<bool> ping(false);
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&ping] {
if (!ping.exchange(false)) {
_exit(0);
}
});
timer.setInterval(11000);
timer.start();
while (true) { while (true) {
char size_c[4]; char size_c[4];
std::cin.read(size_c, 4); std::cin.read(size_c, 4);

View file

@ -195,7 +195,10 @@ void NativeMessagingManager::ReceiverThread::handleMessage(const QJsonObject &ro
} }
#ifdef USEWINSDK #ifdef USEWINSDK
util::postToThread([winId] { widgets::AttachedWindow::detach(winId); }); util::postToThread([winId] {
qDebug() << "NW detach";
widgets::AttachedWindow::detach(winId);
});
#endif #endif
} else { } else {
qDebug() << "NM unknown action " + action; qDebug() << "NM unknown action " + action;

View file

@ -1,15 +1,15 @@
#include "attachedwindow.hpp" #include "attachedwindow.hpp"
#include "application.hpp" #include "application.hpp"
#include "util/debugcount.hpp"
#include "widgets/split.hpp"
#include <QTimer> #include <QTimer>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "widgets/split.hpp"
#ifdef USEWINSDK #ifdef USEWINSDK
#include "Windows.h" #include "Windows.h"
// don't even think about reordering these
#include "Psapi.h" #include "Psapi.h"
#pragma comment(lib, "Dwmapi.lib") #pragma comment(lib, "Dwmapi.lib")
#endif #endif
@ -19,17 +19,19 @@ namespace widgets {
AttachedWindow::AttachedWindow(void *_target, int _yOffset) AttachedWindow::AttachedWindow(void *_target, int _yOffset)
: QWidget(nullptr, Qt::FramelessWindowHint | Qt::Window) : QWidget(nullptr, Qt::FramelessWindowHint | Qt::Window)
, target(_target) , target_(_target)
, yOffset(_yOffset) , yOffset_(_yOffset)
{ {
QLayout *layout = new QVBoxLayout(this); QLayout *layout = new QVBoxLayout(this);
layout->setMargin(0); layout->setMargin(0);
this->setLayout(layout); this->setLayout(layout);
auto *split = new Split(this); auto *split = new Split(this);
this->ui.split = split; this->ui_.split = split;
split->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding); split->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
layout->addWidget(split); layout->addWidget(split);
util::DebugCount::increase("attached window");
} }
AttachedWindow::~AttachedWindow() AttachedWindow::~AttachedWindow()
@ -40,6 +42,8 @@ AttachedWindow::~AttachedWindow()
break; break;
} }
} }
util::DebugCount::decrease("attached window");
} }
AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args) AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args)
@ -64,7 +68,7 @@ AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args)
window->hide(); window->hide();
show = false; show = false;
} else { } else {
window->_height = args.height; window->height_ = args.height;
size.setHeight(args.height); size.setHeight(args.height);
} }
} }
@ -73,14 +77,14 @@ AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args)
window->hide(); window->hide();
show = false; show = false;
} else { } else {
window->_width = args.width; window->width_ = args.width;
size.setWidth(args.width); size.setWidth(args.width);
} }
} }
if (show) { if (show) {
window->updateWindowRect_(window->target_);
window->show(); window->show();
// window->resize(size);
} }
return window; return window;
@ -97,26 +101,30 @@ void AttachedWindow::detach(const QString &winId)
void AttachedWindow::setChannel(ChannelPtr channel) void AttachedWindow::setChannel(ChannelPtr channel)
{ {
this->ui.split->setChannel(channel); this->ui_.split->setChannel(channel);
} }
void AttachedWindow::showEvent(QShowEvent *) void AttachedWindow::showEvent(QShowEvent *)
{ {
attachToHwnd(this->target); attachToHwnd_(this->target_);
} }
void AttachedWindow::attachToHwnd(void *_hwnd) void AttachedWindow::attachToHwnd_(void *_attachedPtr)
{ {
#ifdef USEWINSDK #ifdef USEWINSDK
QTimer *timer = new QTimer(this); if (this->attached_) {
timer->setInterval(1); return;
}
HWND hwnd = HWND(this->winId()); this->attached_ = true;
HWND attached = HWND(_hwnd); this->timer_.setInterval(1);
QObject::connect(timer, &QTimer::timeout, [this, hwnd, attached, timer] { auto hwnd = HWND(this->winId());
auto attached = HWND(_attachedPtr);
QObject::connect(&this->timer_, &QTimer::timeout, [this, hwnd, attached] {
// check process id // check process id
if (!this->validProcessName_) {
DWORD processId; DWORD processId;
::GetWindowThreadProcessId(attached, &processId); ::GetWindowThreadProcessId(attached, &processId);
@ -128,12 +136,24 @@ void AttachedWindow::attachToHwnd(void *_hwnd)
QString qfilename = QString::fromWCharArray(filename.get(), filenameLength); QString qfilename = QString::fromWCharArray(filename.get(), filenameLength);
if (!qfilename.endsWith("chrome.exe")) { if (!qfilename.endsWith("chrome.exe")) {
qDebug() << "NM Illegal callee" << qfilename; qDebug() << "NM Illegal caller" << qfilename;
timer->stop(); this->timer_.stop();
timer->deleteLater();
this->deleteLater(); this->deleteLater();
return; return;
} }
this->validProcessName_ = true;
}
this->updateWindowRect_(attached);
});
this->timer_.start();
#endif
}
void AttachedWindow::updateWindowRect_(void *_attachedPtr)
{
auto hwnd = HWND(this->winId());
auto attached = HWND(_attachedPtr);
// We get the window rect first so we can close this window when it returns an error. // We get the window rect first so we can close this window when it returns an error.
// If we query the process first and check the filename then it will return and empty string // If we query the process first and check the filename then it will return and empty string
@ -143,8 +163,9 @@ void AttachedWindow::attachToHwnd(void *_hwnd)
::GetWindowRect(attached, &rect); ::GetWindowRect(attached, &rect);
if (::GetLastError() != 0) { if (::GetLastError() != 0) {
timer->stop(); qDebug() << "NM GetLastError()" << ::GetLastError();
timer->deleteLater();
this->timer_.stop();
this->deleteLater(); this->deleteLater();
return; return;
} }
@ -155,20 +176,16 @@ void AttachedWindow::attachToHwnd(void *_hwnd)
::SetWindowPos(hwnd, next ? next : HWND_TOPMOST, 0, 0, 0, 0, ::SetWindowPos(hwnd, next ? next : HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
if (this->_height == -1) { if (this->height_ == -1) {
// ::MoveWindow(hwnd, rect.right - this->_width - 8, rect.top + this->yOffset // ::MoveWindow(hwnd, rect.right - this->width_ - 8, rect.top + this->yOffset_ - 8,
// - 8, // this->width_, rect.bottom - rect.top - this->yOffset_, false);
// this->_width, rect.bottom - rect.top - this->yOffset, false);
} else { } else {
::MoveWindow(hwnd, rect.right - this->_width - 8, rect.bottom - this->_height - 8, ::MoveWindow(hwnd, rect.right - this->width_ - 8, rect.bottom - this->height_ - 8,
this->_width, this->_height, false); this->width_, this->height_, false);
} }
// ::MoveWindow(hwnd, rect.right - 360, rect.top + 82, 360 - 8, rect.bottom - // ::MoveWindow(hwnd, rect.right - 360, rect.top + 82, 360 - 8, rect.bottom -
// rect.top - 82 - 8, false); // rect.top - 82 - 8, false);
});
timer->start();
#endif
} }
// void AttachedWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) // void AttachedWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)

View file

@ -10,7 +10,7 @@ namespace widgets {
class AttachedWindow : public QWidget class AttachedWindow : public QWidget
{ {
AttachedWindow(void *target, int asdf); AttachedWindow(void *target_, int asdf);
public: public:
struct GetArgs { struct GetArgs {
@ -20,9 +20,9 @@ public:
int height = -1; int height = -1;
}; };
~AttachedWindow(); virtual ~AttachedWindow() override;
static AttachedWindow *get(void *target, const GetArgs &args); static AttachedWindow *get(void *target_, const GetArgs &args);
static void detach(const QString &winId); static void detach(const QString &winId);
void setChannel(ChannelPtr channel); void setChannel(ChannelPtr channel);
@ -33,17 +33,21 @@ protected:
// override; // override;
private: private:
void *target; void *target_;
int yOffset; int yOffset_;
int currentYOffset; int currentYOffset_;
int _width = 360; int width_ = 360;
int _height = -1; int height_ = -1;
bool validProcessName_ = false;
bool attached_ = false;
QTimer timer_;
struct { struct {
Split *split; Split *split;
} ui; } ui_;
void attachToHwnd(void *hwnd); void attachToHwnd_(void *attached);
void updateWindowRect_(void *attached);
struct Item { struct Item {
void *hwnd; void *hwnd;