File indexing completed on 2024-04-28 05:32:50
0001 /* 0002 Copyright (C) 2017 Kai Uwe Broulik <kde@privat.broulik.de> 0003 0004 This program is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU General Public License as 0006 published by the Free Software Foundation; either version 3 of 0007 the License, or (at your option) any later version. 0008 0009 This program is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 GNU General Public License for more details. 0013 0014 You should have received a copy of the GNU General Public License 0015 along with this program. If not, see <http://www.gnu.org/licenses/>. 0016 */ 0017 0018 function sendEnvironment() { 0019 var browser = ""; 0020 0021 var ua = navigator.userAgent; 0022 // Try to match the most derived first 0023 if (ua.match(/vivaldi/i)) { 0024 browser = "vivaldi"; 0025 } else if(ua.match(/OPR/i)) { 0026 browser = "opera"; 0027 } else if(ua.match(/chrome/i)) { 0028 browser = "chromium"; 0029 // Apparently there is no better way to distinuish chromium from chrome 0030 for (i in window.navigator.plugins) { 0031 if (window.navigator.plugins[i].name === "Chrome PDF Viewer") { 0032 browser = "chrome"; 0033 break; 0034 } 0035 } 0036 } else if(ua.match(/firefox/i)) { 0037 browser = "firefox"; 0038 } 0039 0040 sendPortMessage("settings", "setEnvironment", {browserName: browser}); 0041 } 0042 0043 function sendSettings() { 0044 SettingsUtils.get().then((items) => { 0045 sendPortMessage("settings", "changed", items); 0046 }); 0047 } 0048 0049 // activates giveb tab and raises its window, used by tabs runner and mpris Raise command 0050 function raiseTab(tabId) { 0051 // first activate the tab, this means it's current in its window 0052 chrome.tabs.update(tabId, {active: true}, function (tab) { 0053 0054 if (chrome.runtime.lastError || !tab) { // this "lastError" stuff feels so archaic 0055 // failed to update 0056 return; 0057 } 0058 0059 // then raise the tab's window too 0060 chrome.windows.update(tab.windowId, {focused: true}); 0061 }); 0062 } 0063 0064 // Debug 0065 // ------------------------------------------------------------------------ 0066 // 0067 0068 function printDebug(payload, fn) { 0069 let hostLabel = "Host"; 0070 if (payload.category && payload.category !== "default") { 0071 hostLabel += " [" + payload.category + "]"; 0072 } 0073 0074 const hostLabelColor = "#3daee9"; // Breeze highlight color 0075 0076 if (payload.line && payload.file) { 0077 const fileName = payload.file.split("/").pop(); 0078 fn("%c%s: %c%s %c[%s:%i]", 0079 "color: " + hostLabelColor, 0080 hostLabel, 0081 "", // reset CSS 0082 payload.message, 0083 "color: #999", 0084 fileName, 0085 payload.line); 0086 } else { 0087 fn("%c%s: %c%s", 0088 "color: " + hostLabelColor, 0089 hostLabel, 0090 "", // reset CSS 0091 payload.message); 0092 } 0093 } 0094 0095 addCallback("debug", "debug", function(payload) { 0096 if (payload.severity === "info") { 0097 printDebug(payload, console.info); 0098 } else { 0099 printDebug(payload, console.log); 0100 } 0101 } 0102 ) 0103 0104 addCallback("debug", "warning", function(payload) { 0105 if (payload.severity === "critical" || payload.severity === "fatal") { 0106 printDebug(payload, console.error); 0107 } else { 0108 printDebug(payload, console.warn); 0109 } 0110 } 0111 ) 0112 0113 // System 0114 // ------------------------------------------------------------------------ 0115 // 0116 0117 // When connecting to native host fails (e.g. not installed), we immediately get a disconnect 0118 // event immediately afterwards. Also avoid infinite restart loop then. 0119 var receivedMessageOnce = false; 0120 0121 var portStatus = ""; 0122 var portLastErrorMessage = undefined; 0123 0124 function updateBrowserAction() { 0125 if (portStatus === "UNSUPPORTED_OS" || portStatus === "STARTUP_FAILED") { 0126 chrome.browserAction.setIcon({ 0127 path: { 0128 "16": "icons/plasma-disabled-16.png", 0129 "32": "icons/plasma-disabled-32.png", 0130 "48": "icons/plasma-disabled-48.png", 0131 "128": "icons/plasma-disabled-128.png" 0132 } 0133 }); 0134 } 0135 0136 if (portLastErrorMessage && receivedMessageOnce) { 0137 chrome.browserAction.setBadgeText({ text: "!" }); 0138 chrome.browserAction.setBadgeBackgroundColor({ color: "#da4453" }); // breeze "negative" color 0139 } else { 0140 chrome.browserAction.setBadgeText({ text: "" }); 0141 } 0142 } 0143 updateBrowserAction(); 0144 0145 // Check for supported platform to avoid loading it on e.g. Windows and then failing 0146 // when the extension got synced to another device and then failing 0147 chrome.runtime.getPlatformInfo(function (info) { 0148 if (!SUPPORTED_PLATFORMS.includes(info.os)) { 0149 console.log("This extension is not supported on", info.os); 0150 portStatus = "UNSUPPORTED_OS"; 0151 updateBrowserAction(); 0152 return; 0153 } 0154 0155 connectHost(); 0156 }); 0157 0158 function connectHost() { 0159 port = chrome.runtime.connectNative("org.kde.plasma.browser_integration"); 0160 0161 port.onMessage.addListener(function (message) { 0162 var subsystem = message.subsystem; 0163 var action = message.action; 0164 0165 let isReply = message.hasOwnProperty("replyToSerial"); 0166 let replyToSerial = message.replyToSerial; 0167 0168 if (!isReply && (!subsystem || !action)) { 0169 return; 0170 } 0171 0172 if (portStatus) { 0173 portStatus = ""; 0174 updateBrowserAction(); 0175 } 0176 0177 receivedMessageOnce = true; 0178 0179 if (isReply) { 0180 let replyResolver = pendingMessageReplyResolvers[replyToSerial]; 0181 if (replyResolver) { 0182 replyResolver(message.payload); 0183 delete pendingMessageReplyResolvers[replyToSerial]; 0184 } else { 0185 console.warn("There is no reply resolver for message with serial", replyToSerial); 0186 } 0187 return; 0188 } 0189 0190 if (callbacks[subsystem] && callbacks[subsystem][action]) { 0191 callbacks[subsystem][action](message.payload, action); 0192 } else { 0193 console.warn("Don't know what to do with host message", subsystem, action); 0194 } 0195 }); 0196 0197 port.onDisconnect.addListener(function(port) { 0198 var error = chrome.runtime.lastError; 0199 // Firefox passes in the port which may then have an error set 0200 if (port && port.error) { 0201 error = port.error; 0202 } 0203 0204 console.warn("Host disconnected", error && error.message); 0205 0206 // Remove all kde connect menu entries since they won't work without a host 0207 try { 0208 Object.keys(kdeConnectDevices).forEach((deviceId) => { 0209 callbacks.kdeconnect.deviceRemoved({ 0210 id: deviceId 0211 }); 0212 }); 0213 } catch (e) { 0214 console.warn("Failed to cleanup after port disconnect", e); 0215 } 0216 0217 portLastErrorMessage = error && error.message || "UNKNOWN"; 0218 if (receivedMessageOnce) { 0219 portStatus = "DISCONNECTED"; 0220 0221 console.log("Auto-restarting it"); 0222 connectHost(); 0223 } else { 0224 portStatus = "STARTUP_FAILED"; 0225 0226 console.warn("Not auto-restarting host as we haven't received any message from it before. Check that it's working/installed correctly"); 0227 } 0228 updateBrowserAction(); 0229 }); 0230 0231 sendEnvironment(); 0232 sendSettings(); 0233 sendDownloads(); 0234 0235 updatePurposeMenu(); 0236 } 0237 0238 SettingsUtils.onChanged().addListener(() => { 0239 sendSettings(); 0240 }); 0241 0242 addRuntimeCallback("settings", "openKRunnerSettings", function () { 0243 sendPortMessage("settings", "openKRunnerSettings"); 0244 }); 0245 0246 addRuntimeCallback("settings", "getSubsystemStatus", (message, sender, action) => { 0247 return sendPortMessageWithReply("settings", "getSubsystemStatus"); 0248 }); 0249 0250 addRuntimeCallback("settings", "getVersion", () => { 0251 return sendPortMessageWithReply("settings", "getVersion"); 0252 }); 0253 0254 addRuntimeCallback("browserAction", "getStatus", (message) => { 0255 let info = { 0256 portStatus, 0257 portLastErrorMessage 0258 }; 0259 0260 return Promise.resolve(info); 0261 }); 0262 0263 addRuntimeCallback("browserAction", "ready", () => { 0264 0265 // HACK there's no way to tell whether the browser action popup got closed 0266 // None of onunload, onbeforeunload, onvisibilitychanged are fired. 0267 // Instead, we create a port once the browser action is ready and then 0268 // listen for the port being disconnected. 0269 0270 let browserActionPort = chrome.runtime.connect({ 0271 name: "browserActionPort" 0272 }); 0273 browserActionPort.onDisconnect.addListener((port) => { 0274 if (port.name !== "browserActionPort") { 0275 return; 0276 } 0277 0278 // disabling the browser action immediately when opening it 0279 // causes opening to fail on Firefox, so clear the error only when it's being closed. 0280 // Only clear error when it was a transient error, not a startup failure 0281 if (receivedMessageOnce) { 0282 portLastErrorMessage = ""; 0283 updateBrowserAction(); 0284 } 0285 }); 0286 });