comparison background.js @ 0:2050741e9711

Initial revision
author Guido Berhoerster <guido+cws@berhoerster.name>
date Tue, 18 Sep 2018 22:57:18 +0200
parents
children f77dab12bb52
comparison
equal deleted inserted replaced
-1:000000000000 0:2050741e9711
1 /*
2 * Copyright (C) 2018 Guido Berhoerster <guido+cws@berhoerster.name>
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 */
8
9 'use strict';
10
11 function encodeXML(str) {
12 return str.replace(/[<>&'"]/g, c => {
13 switch (c) {
14 case '<': return '&lt;';
15 case '>': return '&gt;';
16 case '&': return '&amp;';
17 case '\'': return '&apos;';
18 case '"': return '&quot;';
19 }
20 });
21 }
22
23 function createFavIcon(name) {
24 let firstLetter = name.trim().charAt(0);
25 if (firstLetter === '') {
26 firstLetter = '?';
27 }
28 let svg = `<svg viewBox="0 0 16 16" ` +
29 `xmlns="http://www.w3.org/2000/svg">` +
30 `<defs>` +
31 `<filter id="ds">` +
32 `<feDropShadow dx="0" dy="0" stdDeviation=".5"/>` +
33 `</filter>` +
34 `</defs>` +
35 `<rect width="16" height="16" rx="3" ry="3" fill="#ed00b5"/>` +
36 `<text x="8" y="12" alignment-baseline="middle" fill="#fff" ` +
37 `filter="url(#ds)" font-family="sans-serif" font-size="11px" ` +
38 `font-weight="bold" text-anchor="middle">` +
39 `${encodeXML(firstLetter)}</text>` +
40 `</svg>`;
41 return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
42 }
43
44 async function createMenu(isSelection) {
45 let searchEngines = await browser.search.get();
46 let menuItems = new Set();
47 for (let engine of searchEngines) {
48 let favIcon = typeof engine.favIconUrl !== 'undefined' ?
49 engine.favIconUrl : createFavIcon(engine.name);
50 let menuItemId = browser.menus.create({
51 id: engine.name,
52 title: browser.i18n.getMessage(isSelection ?
53 'searchSelectionWithEngine' : 'searchLinkWithEngine',
54 engine.name.trim()),
55 icons: {16: favIcon},
56 contexts: ['link', 'selection']
57 }, async () => {
58 if (browser.runtime.lastError) {
59 console.log(`Failed to create menu item:` +
60 `${browser.runtime.lastError}`);
61 }
62
63 // browser.menus.create does not return a promise that can be
64 // awaited, thus collect the returned menu item IDs in a set and
65 // remove them from this callback which is called when the
66 // asynchronous creation function has finished; when the set is
67 // empty all creation functions have finished and the menu can be
68 // refreshed
69 menuItems.delete(menuItemId);
70 if (menuItems.size === 0) {
71 try {
72 await browser.menus.refresh();
73 } catch (e) {
74 console.log(`Failed to refresh menu: ${e.message}`);
75 }
76 }
77 });
78 menuItems.add(menuItemId);
79 }
80 }
81
82 browser.menus.onShown.addListener(async (info, tab) => {
83 try {
84 await createMenu(info.contexts.includes('selection'));
85 } catch (e) {
86 console.log(e);
87 }
88 });
89
90 browser.menus.onHidden.addListener(async () => {
91 try {
92 await browser.menus.removeAll();
93 } catch (e) {
94 console.log(`Failed to remove menu items: ${e.message}`);
95 }
96 try {
97 await browser.menus.refresh();
98 } catch (e) {
99 console.log(`Failed to refresh menu: ${e.message}`);
100 }
101 });
102
103 browser.menus.onClicked.addListener(async (info, tab) => {
104 let query = typeof info.selectionText !== 'undefined' ?
105 info.selectionText.trim() : info.linkText.trim();
106 try {
107 await browser.search.search({
108 query: query,
109 engine: info.menuItemId
110 });
111 } catch (e) {
112 console.log(`Failed to search for "${query}": ${e.message}`);
113 }
114 });