2 Copyright (C) 2017 Kai Uwe Broulik <kde@privat.broulik.de>
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 3 of
7 the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 function sendEnvironment() {
21 var ua = navigator.userAgent;
22 // Try to match the most derived first
23 if (ua.match(/vivaldi/i)) {
25 } else if(ua.match(/OPR/i)) {
27 } else if(ua.match(/chrome/i)) {
29 // Apparently there is no better way to distinuish chromium from chrome
30 for (i in window.navigator.plugins) {
31 if (window.navigator.plugins[i].name === "Chrome PDF Viewer") {
36 } else if(ua.match(/firefox/i)) {
40 sendPortMessage("settings", "setEnvironment", {browserName: browser});
43 function sendSettings() {
44 SettingsUtils.get().then((items) => {
45 sendPortMessage("settings", "changed", items);
49 // activates giveb tab and raises its window, used by tabs runner and mpris Raise command
50 function raiseTab(tabId) {
51 // first activate the tab, this means it's current in its window
52 chrome.tabs.update(tabId, {active: true}, function (tab) {
54 if (chrome.runtime.lastError || !tab) { // this "lastError" stuff feels so archaic
59 // then raise the tab's window too
60 chrome.windows.update(tab.windowId, {focused: true});
65 // ------------------------------------------------------------------------
67 addCallback("debug", "debug", function(payload) {
68 console.log("From host:", payload.message);
72 addCallback("debug", "warning", function(payload) {
73 console.warn("From host:", payload.message);
78 // ------------------------------------------------------------------------
81 // When connecting to native host fails (e.g. not installed), we immediately get a disconnect
82 // event immediately afterwards. Also avoid infinite restart loop then.
83 var receivedMessageOnce = false;
86 var portLastErrorMessage = undefined;
88 function updateBrowserAction() {
89 if (portStatus === "UNSUPPORTED_OS" || portStatus === "STARTUP_FAILED") {
90 chrome.browserAction.setIcon({
92 "16": "icons/plasma-disabled-16.png",
93 "32": "icons/plasma-disabled-32.png",
94 "48": "icons/plasma-disabled-48.png",
95 "128": "icons/plasma-disabled-128.png"
100 if (portLastErrorMessage && receivedMessageOnce) {
101 chrome.browserAction.setBadgeText({ text: "!" });
102 chrome.browserAction.setBadgeBackgroundColor({ color: "#da4453" }); // breeze "negative" color
104 chrome.browserAction.setBadgeText({ text: "" });
107 updateBrowserAction();
109 // Check for supported platform to avoid loading it on e.g. Windows and then failing
110 // when the extension got synced to another device and then failing
111 chrome.runtime.getPlatformInfo(function (info) {
112 if (!SUPPORTED_PLATFORMS.includes(info.os)) {
113 console.log("This extension is not supported on", info.os);
114 portStatus = "UNSUPPORTED_OS";
115 updateBrowserAction();
122 function connectHost() {
123 port = chrome.runtime.connectNative("org.kde.plasma.browser_integration");
125 port.onMessage.addListener(function (message) {
126 var subsystem = message.subsystem;
127 var action = message.action;
129 let isReply = message.hasOwnProperty("replyToSerial");
130 let replyToSerial = message.replyToSerial;
132 if (!isReply && (!subsystem || !action)) {
138 updateBrowserAction();
141 receivedMessageOnce = true;
144 let replyResolver = pendingMessageReplyResolvers[replyToSerial];
146 replyResolver(message.payload);
147 delete pendingMessageReplyResolvers[replyToSerial];
149 console.warn("There is no reply resolver for message with serial", replyToSerial);
154 if (callbacks[subsystem] && callbacks[subsystem][action]) {
155 callbacks[subsystem][action](message.payload, action);
157 console.warn("Don't know what to do with host message", subsystem, action);
161 port.onDisconnect.addListener(function(port) {
162 var error = chrome.runtime.lastError;
163 // Firefox passes in the port which may then have an error set
164 if (port && port.error) {
168 console.warn("Host disconnected", error && error.message);
170 // Remove all kde connect menu entries since they won't work without a host
172 Object.keys(kdeConnectDevices).forEach((deviceId) => {
173 callbacks.kdeconnect.deviceRemoved({
178 console.warn("Failed to cleanup after port disconnect", e);
181 portLastErrorMessage = error && error.message || "UNKNOWN";
182 if (receivedMessageOnce) {
183 portStatus = "DISCONNECTED";
185 console.log("Auto-restarting it");
188 portStatus = "STARTUP_FAILED";
190 console.warn("Not auto-restarting host as we haven't received any message from it before. Check that it's working/installed correctly");
192 updateBrowserAction();
202 SettingsUtils.onChanged().addListener(() => {
206 addRuntimeCallback("settings", "openKRunnerSettings", function () {
207 sendPortMessage("settings", "openKRunnerSettings");
210 addRuntimeCallback("settings", "getSubsystemStatus", (message, sender, action) => {
211 return sendPortMessageWithReply("settings", "getSubsystemStatus");
214 addRuntimeCallback("settings", "getVersion", () => {
215 return sendPortMessageWithReply("settings", "getVersion");
218 addRuntimeCallback("browserAction", "getStatus", (message) => {
224 return Promise.resolve(info);
227 addRuntimeCallback("browserAction", "ready", () => {
229 // HACK there's no way to tell whether the browser action popup got closed
230 // None of onunload, onbeforeunload, onvisibilitychanged are fired.
231 // Instead, we create a port once the browser action is ready and then
232 // listen for the port being disconnected.
234 let browserActionPort = chrome.runtime.connect({
235 name: "browserActionPort"
237 browserActionPort.onDisconnect.addListener((port) => {
238 if (port.name !== "browserActionPort") {
242 // disabling the browser action immediately when opening it
243 // causes opening to fail on Firefox, so clear the error only when it's being closed.
244 // Only clear error when it was a transient error, not a startup failure
245 if (receivedMessageOnce) {
246 portLastErrorMessage = "";
247 updateBrowserAction();