Warning, file /plasma/plasma-browser-integration/extension/extension-purpose.js was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     Copyright (C) 2019 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 const purposeShareMenuId = "purpose_share";
0019 let hasPurposeMenu = false;
0020 
0021 // Stores <notification id, share url> so that when you click the finished
0022 // notification it will open the URL
0023 let purposeNotificationUrls = {};
0024 
0025 function purposeShare(data) {
0026     return new Promise((resolve, reject) => {
0027         sendPortMessageWithReply("purpose", "share", {data}).then((reply) => {
0028             if (!reply.success) {
0029                 if (!["BUSY", "CANCELED", "INVALID_ARGUMENT"].includes(reply.errorCode)
0030                     && reply.errorCode !== 1 /*ERR_USER_CANCELED*/) {
0031                     chrome.notifications.create(null, {
0032                         type: "basic",
0033                         title: chrome.i18n.getMessage("purpose_share_failed_title"),
0034                         message: chrome.i18n.getMessage("purpose_share_failed_text",
0035                                                         reply.errorMessage || chrome.i18n.getMessage("general_error_unknown")),
0036                         iconUrl: "icons/document-share-failed.png"
0037                     });
0038                 }
0039 
0040                 reject();
0041                 return;
0042             }
0043 
0044             let url = reply.response.url;
0045             if (url) {
0046                 chrome.notifications.create(null, {
0047                     type: "basic",
0048                     title: chrome.i18n.getMessage("purpose_share_finished_title"),
0049                     message: chrome.i18n.getMessage("purpose_share_finished_text", url),
0050                     iconUrl: "icons/document-share.png"
0051                 }, (notificationId) => {
0052                     if (chrome.runtime.lastError) {
0053                         return;
0054                     }
0055 
0056                     purposeNotificationUrls[notificationId] = url;
0057                 });
0058             }
0059 
0060             resolve();
0061         });
0062     });
0063 }
0064 
0065 function checkPurposeEnabled() {
0066     return Promise.all([
0067         sendPortMessageWithReply("settings", "getSubsystemStatus"),
0068         SettingsUtils.get()
0069     ]).then((result) => {
0070 
0071         const subsystemStatus = result[0];
0072         const settings = result[1];
0073 
0074         // HACK Unfortunately I removed the loaded/unloaded signals for plugins
0075         // so we can't reliably know on settings change whether a module is enabled
0076         // sending settings is also legacy done without a reply we could wait for.
0077         // Instead, check whether the module is known and enabled in settings,
0078         // which should be close enough, since purpose plugin also has no additional
0079         // dependencies that could make it fail to load.
0080         return subsystemStatus.hasOwnProperty("purpose")
0081             && settings.purpose && settings.purpose.enabled;
0082     });
0083 }
0084 
0085 function updatePurposeMenu() {
0086     checkPurposeEnabled().then((enabled) => {
0087         if (enabled && !hasPurposeMenu) {
0088             let props = {
0089                 id: purposeShareMenuId,
0090                 contexts: ["link", "page", "image", "audio", "video", "selection"],
0091                 title: chrome.i18n.getMessage("purpose_share")
0092             };
0093 
0094             if (IS_FIREFOX) {
0095                 props.icons = {
0096                     "16": "icons/document-share-symbolic.svg"
0097                 }
0098             }
0099 
0100             chrome.contextMenus.create(props, () => {
0101                 const error = chrome.runtime.lastError;
0102                 if (error) {
0103                     console.warn("Error creating purpose context menu", error.message);
0104                     return;
0105                 }
0106                 hasPurposeMenu = true;
0107             });
0108         } else if (!enabled && hasPurposeMenu) {
0109             chrome.contextMenus.remove(purposeShareMenuId, () => {
0110                 const error = chrome.runtime.lastError;
0111                 if (error) {
0112                     console.warn("Error removing purpose context menu", error.message);
0113                     return;
0114                 }
0115                 hasPurposeMenu = false;
0116             });
0117         }
0118     });
0119 }
0120 
0121 chrome.contextMenus.onClicked.addListener((info) => {
0122     if (info.menuItemId !== purposeShareMenuId) {
0123         return;
0124     }
0125 
0126     let url = info.linkUrl || info.srcUrl || info.pageUrl;
0127     let selection = info.selectionText;
0128     if (!url && !selection) {
0129         return;
0130     }
0131 
0132     let shareData = {};
0133     if (selection) {
0134         shareData.text = selection;
0135     } else if (url) {
0136         shareData.url = url;
0137         if (info.linkText && info.linkText != url) {
0138             shareData.title = info.linkText;
0139         }
0140     }
0141 
0142     // We probably shared the current page, add its title to shareData
0143     new Promise((resolve, reject) => {
0144         if (!info.linkUrl && !info.srcUrl && info.pageUrl) {
0145             chrome.tabs.query({
0146                 // more correct would probably be currentWindow + activeTab
0147                 url: info.pageUrl
0148             }, (tabs) => {
0149                 if (tabs[0]) {
0150                     return resolve(tabs[0].title);
0151                 }
0152                 resolve("");
0153             });
0154             return;
0155         }
0156 
0157         resolve("");
0158     }).then((title) => {
0159         if (title) {
0160             shareData.title = title;
0161         }
0162 
0163         purposeShare(shareData);
0164     });
0165 });
0166 
0167 SettingsUtils.onChanged().addListener((delta) => {
0168     if (delta.purpose) {
0169         updatePurposeMenu();
0170     }
0171 });
0172 
0173 addRuntimeCallback("purpose", "share", (message, sender, action) => {
0174     return purposeShare(message);
0175 });
0176 
0177 chrome.notifications.onClicked.addListener((notificationId) => {
0178     const url = purposeNotificationUrls[notificationId];
0179     if (url) {
0180         chrome.tabs.create({url});
0181     }
0182 });
0183 
0184 chrome.notifications.onClosed.addListener((notificationId) => {
0185     delete purposeNotificationUrls[notificationId];
0186 });