/* 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/. */ /* globals ContentSearchUIController, ContentSearchHandoffUIController */ "use strict"; import { actionCreators as ac, actionTypes as at, } from "common/Actions.sys.mjs"; import { connect } from "react-redux"; import { IS_NEWTAB } from "content-src/lib/constants"; import React from "react"; export class _Search extends React.PureComponent { constructor(props) { super(props); this.onSearchClick = this.onSearchClick.bind(this); this.onSearchHandoffClick = this.onSearchHandoffClick.bind(this); this.onSearchHandoffPaste = this.onSearchHandoffPaste.bind(this); this.onSearchHandoffDrop = this.onSearchHandoffDrop.bind(this); this.onInputMount = this.onInputMount.bind(this); this.onInputMountHandoff = this.onInputMountHandoff.bind(this); this.onSearchHandoffButtonMount = this.onSearchHandoffButtonMount.bind(this); } handleEvent(event) { // Also track search events with our own telemetry if (event.detail.type === "Search") { this.props.dispatch(ac.UserEvent({ event: "SEARCH" })); } } onSearchClick(event) { window.gContentSearchController.search(event); } doSearchHandoff(text) { this.props.dispatch( ac.OnlyToMain({ type: at.HANDOFF_SEARCH_TO_AWESOMEBAR, data: { text } }) ); this.props.dispatch({ type: at.FAKE_FOCUS_SEARCH }); this.props.dispatch(ac.UserEvent({ event: "SEARCH_HANDOFF" })); if (text) { this.props.dispatch({ type: at.DISABLE_SEARCH }); } } onSearchHandoffClick(event) { // When search hand-off is enabled, we render a big button that is styled to // look like a search textbox. If the button is clicked, we style // the button as if it was a focused search box and show a fake cursor but // really focus the awesomebar without the focus styles ("hidden focus"). event.preventDefault(); this.doSearchHandoff(); } onSearchHandoffPaste(event) { event.preventDefault(); this.doSearchHandoff(event.clipboardData.getData("Text")); } onSearchHandoffDrop(event) { event.preventDefault(); let text = event.dataTransfer.getData("text"); if (text) { this.doSearchHandoff(text); } } componentWillUnmount() { delete window.gContentSearchController; } onInputMount(input) { if (input) { // The "healthReportKey" and needs to be "newtab" or "abouthome" so that // BrowserUsageTelemetry.jsm knows to handle events with this name, and // can add the appropriate telemetry probes for search. Without the correct // name, certain tests like browser_UsageTelemetry_content.js will fail // (See github ticket #2348 for more details) const healthReportKey = IS_NEWTAB ? "newtab" : "abouthome"; // The "searchSource" needs to be "newtab" or "homepage" and is sent with // the search data and acts as context for the search request (See // nsISearchEngine.getSubmission). It is necessary so that search engine // plugins can correctly atribute referrals. (See github ticket #3321 for // more details) const searchSource = IS_NEWTAB ? "newtab" : "homepage"; // gContentSearchController needs to exist as a global so that tests for // the existing about:home can find it; and so it allows these tests to pass. // In the future, when activity stream is default about:home, this can be renamed window.gContentSearchController = new ContentSearchUIController( input, input.parentNode, healthReportKey, searchSource ); addEventListener("ContentSearchClient", this); } else { window.gContentSearchController = null; removeEventListener("ContentSearchClient", this); } } onInputMountHandoff(input) { if (input) { // The handoff UI controller helps us set the search icon and reacts to // changes to default engine to keep everything in sync. this._handoffSearchController = new ContentSearchHandoffUIController(); } } getDefaultEngineName() { // _handoffSearchController will manage engine names once it is initialized. return this.props.Prefs.values["urlbar.placeholderName"]; } getHandoffInputL10nAttributes() { let defaultEngineName = this.getDefaultEngineName(); return defaultEngineName ? { "data-l10n-id": "newtab-search-box-handoff-input", "data-l10n-args": `{"engine": "${defaultEngineName}"}`, } : { "data-l10n-id": "newtab-search-box-handoff-input-no-engine", }; } getHandoffTextL10nAttributes() { let defaultEngineName = this.getDefaultEngineName(); return defaultEngineName ? { "data-l10n-id": "newtab-search-box-handoff-text", "data-l10n-args": `{"engine": "${defaultEngineName}"}`, } : { "data-l10n-id": "newtab-search-box-handoff-text-no-engine", }; } onSearchHandoffButtonMount(button) { // Keep a reference to the button for use during "paste" event handling. this._searchHandoffButton = button; } /* * Do not change the ID on the input field, as legacy newtab code * specifically looks for the id 'newtab-search-text' on input fields * in order to execute searches in various tests */ render() { const wrapperClassName = [ "search-wrapper", this.props.disable && "search-disabled", this.props.fakeFocus && "fake-focus", ] .filter(v => v) .join(" "); return (
{this.props.showLogo && (
)} {!this.props.handoffEnabled && (
)} {this.props.handoffEnabled && (
)}
); } } export const Search = connect(state => ({ Prefs: state.Prefs, }))(_Search);