forked from mirrors/gecko-dev
		
	 73e7350295
			
		
	
	
		73e7350295
		
	
	
	
	
		
			
			We can't use context.isPrivate because sometimes context is undefined. I filed https://bugzilla.mozilla.org/show_bug.cgi?id=1841762 about that. Differential Revision: https://phabricator.services.mozilla.com/D182772
		
			
				
	
	
		
			278 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| import {
 | |
|   UrlbarProvider,
 | |
|   UrlbarUtils,
 | |
| } from "resource:///modules/UrlbarUtils.sys.mjs";
 | |
| 
 | |
| const lazy = {};
 | |
| 
 | |
| ChromeUtils.defineESModuleGetters(lazy, {
 | |
|   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
 | |
|   OpenSearchEngine: "resource://gre/modules/OpenSearchEngine.sys.mjs",
 | |
|   UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
 | |
|   UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
 | |
|   UrlbarSearchUtils: "resource:///modules/UrlbarSearchUtils.sys.mjs",
 | |
|   UrlbarView: "resource:///modules/UrlbarView.sys.mjs",
 | |
| });
 | |
| 
 | |
| const DYNAMIC_RESULT_TYPE = "contextualSearch";
 | |
| 
 | |
| const ENABLED_PREF = "contextualSearch.enabled";
 | |
| 
 | |
| const VIEW_TEMPLATE = {
 | |
|   attributes: {
 | |
|     selectable: true,
 | |
|   },
 | |
|   children: [
 | |
|     {
 | |
|       name: "no-wrap",
 | |
|       tag: "span",
 | |
|       classList: ["urlbarView-no-wrap"],
 | |
|       children: [
 | |
|         {
 | |
|           name: "icon",
 | |
|           tag: "img",
 | |
|           classList: ["urlbarView-favicon"],
 | |
|         },
 | |
|         {
 | |
|           name: "search",
 | |
|           tag: "span",
 | |
|           classList: ["urlbarView-title"],
 | |
|         },
 | |
|         {
 | |
|           name: "separator",
 | |
|           tag: "span",
 | |
|           classList: ["urlbarView-title-separator"],
 | |
|         },
 | |
|         {
 | |
|           name: "description",
 | |
|           tag: "span",
 | |
|         },
 | |
|       ],
 | |
|     },
 | |
|   ],
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * A provider that returns an option for using the search engine provided
 | |
|  * by the active view if it utilizes OpenSearch.
 | |
|  */
 | |
| class ProviderContextualSearch extends UrlbarProvider {
 | |
|   constructor() {
 | |
|     super();
 | |
|     this.engines = new Map();
 | |
|     lazy.UrlbarResult.addDynamicResultType(DYNAMIC_RESULT_TYPE);
 | |
|     lazy.UrlbarView.addDynamicViewTemplate(DYNAMIC_RESULT_TYPE, VIEW_TEMPLATE);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Unique name for the provider, used by the context to filter on providers.
 | |
|    * Not using a unique name will cause the newest registration to win.
 | |
|    *
 | |
|    * @returns {string}
 | |
|    */
 | |
|   get name() {
 | |
|     return "UrlbarProviderContextualSearch";
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * The type of the provider.
 | |
|    *
 | |
|    * @returns {UrlbarUtils.PROVIDER_TYPE}
 | |
|    */
 | |
|   get type() {
 | |
|     return UrlbarUtils.PROVIDER_TYPE.PROFILE;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Whether this provider should be invoked for the given context.
 | |
|    * If this method returns false, the providers manager won't start a query
 | |
|    * with this provider, to save on resources.
 | |
|    *
 | |
|    * @param {UrlbarQueryContext} queryContext The query context object
 | |
|    * @returns {boolean} Whether this provider should be invoked for the search.
 | |
|    */
 | |
|   isActive(queryContext) {
 | |
|     return (
 | |
|       queryContext.trimmedSearchString &&
 | |
|       !queryContext.searchMode &&
 | |
|       lazy.UrlbarPrefs.get(ENABLED_PREF)
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Starts querying. Extended classes should return a Promise resolved when the
 | |
|    * provider is done searching AND returning results.
 | |
|    *
 | |
|    * @param {UrlbarQueryContext} queryContext The query context object
 | |
|    * @param {Function} addCallback Callback invoked by the provider to add a new
 | |
|    *        result. A UrlbarResult should be passed to it.
 | |
|    */
 | |
|   async startQuery(queryContext, addCallback) {
 | |
|     let engine;
 | |
|     const hostname =
 | |
|       queryContext?.currentPage && new URL(queryContext.currentPage).hostname;
 | |
| 
 | |
|     // This happens on about pages, which won't have associated engines
 | |
|     if (!hostname) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // First check to see if there's a cached search engine for the host.
 | |
|     // If not, check to see if an installed engine matches the current view.
 | |
|     if (this.engines.has(hostname)) {
 | |
|       engine = this.engines.get(hostname);
 | |
|     } else {
 | |
|       // Strip www. to allow for partial matches when looking for an engine.
 | |
|       const [host] = UrlbarUtils.stripPrefixAndTrim(hostname, {
 | |
|         stripWww: true,
 | |
|       });
 | |
|       engine = (
 | |
|         await lazy.UrlbarSearchUtils.enginesForDomainPrefix(host, {
 | |
|           matchAllDomainLevels: true,
 | |
|           onlyEnabled: false,
 | |
|         })
 | |
|       )[0];
 | |
|     }
 | |
| 
 | |
|     if (engine) {
 | |
|       this.engines.set(hostname, engine);
 | |
|       // Check to see if the engine that was found is the default engine.
 | |
|       // The default engine will often be used to populate the heuristic result,
 | |
|       // and we want to avoid ending up with two nearly identical search results.
 | |
|       let defaultEngine = lazy.UrlbarSearchUtils.getDefaultEngine();
 | |
|       if (engine.name === defaultEngine?.name) {
 | |
|         return;
 | |
|       }
 | |
|       const [url] = UrlbarUtils.getSearchQueryUrl(
 | |
|         engine,
 | |
|         queryContext.searchString
 | |
|       );
 | |
|       let result = this.makeResult({
 | |
|         url,
 | |
|         engine: engine.name,
 | |
|         icon: engine.iconURI?.spec,
 | |
|         input: queryContext.searchString,
 | |
|         shouldNavigate: true,
 | |
|       });
 | |
|       addCallback(this, result);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // If the current view has engines that haven't been added, return a result
 | |
|     // that will first add an engine, then use it to search.
 | |
|     let window = lazy.BrowserWindowTracker.getTopWindow();
 | |
|     let engineToAdd = window?.gBrowser.selectedBrowser?.engines?.[0];
 | |
| 
 | |
|     if (engineToAdd) {
 | |
|       let result = this.makeResult({
 | |
|         hostname,
 | |
|         url: engineToAdd.uri,
 | |
|         engine: engineToAdd.title,
 | |
|         icon: engineToAdd.icon,
 | |
|         input: queryContext.searchString,
 | |
|         shouldAddEngine: true,
 | |
|       });
 | |
|       addCallback(this, result);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   makeResult({
 | |
|     engine,
 | |
|     icon,
 | |
|     url,
 | |
|     input,
 | |
|     hostname,
 | |
|     shouldNavigate = false,
 | |
|     shouldAddEngine = false,
 | |
|   }) {
 | |
|     let result = new lazy.UrlbarResult(
 | |
|       UrlbarUtils.RESULT_TYPE.DYNAMIC,
 | |
|       UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
 | |
|       {
 | |
|         engine,
 | |
|         icon,
 | |
|         url,
 | |
|         input,
 | |
|         hostname,
 | |
|         shouldAddEngine,
 | |
|         shouldNavigate,
 | |
|         dynamicType: DYNAMIC_RESULT_TYPE,
 | |
|       }
 | |
|     );
 | |
|     result.suggestedIndex = -1;
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * This is called when the urlbar view updates the view of one of the results
 | |
|    * of the provider.  It should return an object describing the view update.
 | |
|    * See the base UrlbarProvider class for more.
 | |
|    *
 | |
|    * @param {UrlbarResult} result The result whose view will be updated.
 | |
|    * @param {Map} idsByName
 | |
|    *   A Map from an element's name, as defined by the provider; to its ID in
 | |
|    *   the DOM, as defined by the browser.
 | |
|    * @returns {object} An object describing the view update.
 | |
|    */
 | |
|   getViewUpdate(result, idsByName) {
 | |
|     return {
 | |
|       icon: {
 | |
|         attributes: {
 | |
|           src: result.payload.icon || UrlbarUtils.ICON.SEARCH_GLASS,
 | |
|         },
 | |
|       },
 | |
|       search: {
 | |
|         textContent: result.payload.input,
 | |
|         attributes: {
 | |
|           title: result.payload.input,
 | |
|         },
 | |
|       },
 | |
|       description: {
 | |
|         l10n: {
 | |
|           id: "urlbar-result-action-search-w-engine",
 | |
|           args: {
 | |
|             engine: result.payload.engine,
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   onEngagement(state, queryContext, details, controller) {
 | |
|     let { result } = details;
 | |
|     if (result?.providerName == this.name) {
 | |
|       this.#pickResult(result, controller.browserWindow);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async #pickResult(result, window) {
 | |
|     // If we have an engine to add, first create a new OpenSearchEngine, then
 | |
|     // get and open a url to execute a search for the term in the url bar.
 | |
|     // In cases where we don't have to create a new engine, navigation is
 | |
|     // handled automatically by providing `shouldNavigate: true` in the result.
 | |
|     if (result.payload.shouldAddEngine) {
 | |
|       let newEngine = new lazy.OpenSearchEngine({ shouldPersist: false });
 | |
|       newEngine._setIcon(result.payload.icon, false);
 | |
|       await new Promise(resolve => {
 | |
|         newEngine.install(result.payload.url, errorCode => {
 | |
|           resolve(errorCode);
 | |
|         });
 | |
|       });
 | |
|       this.engines.set(result.payload.hostname, newEngine);
 | |
|       const [url] = UrlbarUtils.getSearchQueryUrl(
 | |
|         newEngine,
 | |
|         result.payload.input
 | |
|       );
 | |
|       window.gBrowser.fixupAndLoadURIString(url, {
 | |
|         triggeringPrincipal:
 | |
|           Services.scriptSecurityManager.getSystemPrincipal(),
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| export var UrlbarProviderContextualSearch = new ProviderContextualSearch();
 |