ext-search.js (4069B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ 6 /* vim: set sts=2 sw=2 et tw=80: */ 7 8 "use strict"; 9 10 ChromeUtils.defineESModuleGetters(this, { 11 SearchUIUtils: "moz-src:///browser/components/search/SearchUIUtils.sys.mjs", 12 }); 13 14 var { ExtensionError } = ExtensionUtils; 15 16 const dispositionMap = { 17 CURRENT_TAB: "current", 18 NEW_TAB: "tab", 19 NEW_WINDOW: "window", 20 }; 21 22 this.search = class extends ExtensionAPI { 23 getAPI(context) { 24 function getTarget({ tabId, disposition, defaultDisposition }) { 25 let tab, where; 26 if (disposition) { 27 if (tabId) { 28 throw new ExtensionError(`Cannot set both 'disposition' and 'tabId'`); 29 } 30 where = dispositionMap[disposition]; 31 } else if (tabId) { 32 tab = tabTracker.getTab(tabId); 33 } else { 34 where = dispositionMap[defaultDisposition]; 35 } 36 return { tab, where }; 37 } 38 39 return { 40 search: { 41 async get() { 42 await Services.search.promiseInitialized; 43 let visibleEngines = await Services.search.getVisibleEngines(); 44 let defaultEngine = await Services.search.getDefault(); 45 return Promise.all( 46 visibleEngines.map(async engine => { 47 let favIconUrl = await engine.getIconURL(); 48 // Convert blob:-URLs to data:-URLs since they can't be shared 49 // across processes. blob:-URLs originate from application provided 50 // search engines. 51 // Also convert moz-extension:-URLs to data:-URLs to make sure that 52 // extensions can see icons from other extensions, even if they 53 // are not web-accessible. 54 // Also prevents leakage of extension UUIDs to other extensions.. 55 if ( 56 favIconUrl && 57 (favIconUrl.startsWith("blob:") || 58 (ExtensionUtils.isExtensionUrl(favIconUrl) && 59 !favIconUrl.startsWith(context.extension.baseURL))) 60 ) { 61 favIconUrl = await ExtensionUtils.makeDataURI(favIconUrl); 62 } 63 64 return { 65 name: engine.name, 66 isDefault: engine.name === defaultEngine.name, 67 alias: engine.alias || undefined, 68 favIconUrl, 69 }; 70 }) 71 ); 72 }, 73 74 async search(searchProperties) { 75 await Services.search.promiseInitialized; 76 let engine; 77 78 if (searchProperties.engine) { 79 engine = Services.search.getEngineByName(searchProperties.engine); 80 if (!engine) { 81 throw new ExtensionError( 82 `${searchProperties.engine} was not found` 83 ); 84 } 85 } 86 87 let { tab, where } = getTarget({ 88 tabId: searchProperties.tabId, 89 disposition: searchProperties.disposition, 90 defaultDisposition: "NEW_TAB", 91 }); 92 93 await SearchUIUtils.loadSearch({ 94 window: windowTracker.topWindow, 95 searchText: searchProperties.query, 96 where, 97 engine, 98 tab, 99 triggeringPrincipal: context.principal, 100 sapSource: "webextension", 101 }); 102 }, 103 104 async query(queryProperties) { 105 await Services.search.promiseInitialized; 106 107 let { tab, where } = getTarget({ 108 tabId: queryProperties.tabId, 109 disposition: queryProperties.disposition, 110 defaultDisposition: "CURRENT_TAB", 111 }); 112 113 await SearchUIUtils.loadSearch({ 114 window: windowTracker.topWindow, 115 searchText: queryProperties.text, 116 where, 117 tab, 118 triggeringPrincipal: context.principal, 119 sapSource: "webextension", 120 }); 121 }, 122 }, 123 }; 124 } 125 };