--- /dev/null
+/*
+ Copyright (C) 2019 Kai Uwe Broulik <kde@privat.broulik.de>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+const purposeShareMenuId = "purpose_share";
+let hasPurposeMenu = false;
+
+// Stores <notification id, share url> so that when you click the finished
+// notification it will open the URL
+let purposeNotificationUrls = {};
+
+function purposeShare(data) {
+ return new Promise((resolve, reject) => {
+ sendPortMessageWithReply("purpose", "share", {data}).then((reply) => {
+ if (!reply.success) {
+ if (!["BUSY", "CANCELED", "INVALID_ARGUMENT"].includes(reply.errorCode)
+ && reply.errorCode !== 1 /*ERR_USER_CANCELED*/) {
+ chrome.notifications.create(null, {
+ type: "basic",
+ title: chrome.i18n.getMessage("purpose_share_failed_title"),
+ message: chrome.i18n.getMessage("purpose_share_failed_text",
+ reply.errorMessage || chrome.i18n.getMessage("general_error_unknown")),
+ iconUrl: "icons/document-share-failed.png"
+ });
+ }
+
+ reject();
+ return;
+ }
+
+ let url = reply.response.url;
+ if (url) {
+ chrome.notifications.create(null, {
+ type: "basic",
+ title: chrome.i18n.getMessage("purpose_share_finished_title"),
+ message: chrome.i18n.getMessage("purpose_share_finished_text", url),
+ iconUrl: "icons/document-share.png"
+ }, (notificationId) => {
+ if (chrome.runtime.lastError) {
+ return;
+ }
+
+ purposeNotificationUrls[notificationId] = url;
+ });
+ }
+
+ resolve();
+ });
+ });
+}
+
+function checkPurposeEnabled() {
+ return Promise.all([
+ sendPortMessageWithReply("settings", "getSubsystemStatus"),
+ SettingsUtils.get()
+ ]).then((result) => {
+
+ const subsystemStatus = result[0];
+ const settings = result[1];
+
+ // HACK Unfortunately I removed the loaded/unloaded signals for plugins
+ // so we can't reliably know on settings change whether a module is enabled
+ // sending settings is also legacy done without a reply we could wait for.
+ // Instead, check whether the module is known and enabled in settings,
+ // which should be close enough, since purpose plugin also has no additional
+ // dependencies that could make it fail to load.
+ return subsystemStatus.hasOwnProperty("purpose")
+ && settings.purpose && settings.purpose.enabled;
+ });
+}
+
+function updatePurposeMenu() {
+ checkPurposeEnabled().then((enabled) => {
+ if (enabled && !hasPurposeMenu) {
+ let props = {
+ id: purposeShareMenuId,
+ contexts: ["link", "page", "image", "audio", "video", "selection"],
+ title: chrome.i18n.getMessage("purpose_share")
+ };
+
+ if (IS_FIREFOX) {
+ props.icons = {
+ "16": "icons/document-share-symbolic.svg"
+ }
+ }
+
+ chrome.contextMenus.create(props, () => {
+ const error = chrome.runtime.lastError;
+ if (error) {
+ console.warn("Error creating purpose context menu", error.message);
+ return;
+ }
+ hasPurposeMenu = true;
+ });
+ } else if (!enabled && hasPurposeMenu) {
+ chrome.contextMenus.remove(purposeShareMenuId, () => {
+ const error = chrome.runtime.lastError;
+ if (error) {
+ console.warn("Error removing purpose context menu", error.message);
+ return;
+ }
+ hasPurposeMenu = false;
+ });
+ }
+ });
+}
+
+chrome.contextMenus.onClicked.addListener((info) => {
+ if (info.menuItemId !== purposeShareMenuId) {
+ return;
+ }
+
+ let url = info.linkUrl || info.srcUrl || info.pageUrl;
+ let selection = info.selectionText;
+ if (!url && !selection) {
+ return;
+ }
+
+ let shareData = {};
+ if (selection) {
+ shareData.text = selection;
+ } else if (url) {
+ shareData.url = url;
+ if (info.linkText && info.linkText != url) {
+ shareData.title = info.linkText;
+ }
+ }
+
+ // We probably shared the current page, add its title to shareData
+ new Promise((resolve, reject) => {
+ if (!info.linkUrl && !info.srcUrl && info.pageUrl) {
+ chrome.tabs.query({
+ // more correct would probably be currentWindow + activeTab
+ url: info.pageUrl
+ }, (tabs) => {
+ if (tabs[0]) {
+ return resolve(tabs[0].title);
+ }
+ resolve("");
+ });
+ return;
+ }
+
+ resolve("");
+ }).then((title) => {
+ if (title) {
+ shareData.title = title;
+ }
+
+ purposeShare(shareData);
+ });
+});
+
+SettingsUtils.onChanged().addListener((delta) => {
+ if (delta.purpose) {
+ updatePurposeMenu();
+ }
+});
+
+addRuntimeCallback("purpose", "share", (message, sender, action) => {
+ return purposeShare(message);
+});
+
+chrome.notifications.onClicked.addListener((notificationId) => {
+ const url = purposeNotificationUrls[notificationId];
+ if (url) {
+ chrome.tabs.create({url});
+ }
+});
+
+chrome.notifications.onClosed.addListener((notificationId) => {
+ delete purposeNotificationUrls[notificationId];
+});