├── .github └── workflows │ └── main.yml ├── .gitmodules ├── COPYING.txt ├── Makefile ├── README.md ├── TODO.txt ├── history.en.md ├── history.ja.md ├── licenses └── MPL2.0.txt └── webextensions ├── .gitignore ├── Makefile ├── _locales ├── en │ └── messages.json └── ja │ └── messages.json ├── background └── background.js ├── common ├── common.js ├── constants.js └── permissions.js ├── eslint.config.mjs ├── extlib └── .gitkeep ├── icon-source ├── 16x16-dark.ai ├── 16x16-light.ai ├── 32x32-dark.ai └── 32x32-light.ai ├── manifest.json ├── options ├── init.js ├── options.css └── options.html ├── package-lock.json ├── package.json ├── panel ├── .eslintrc.js ├── panel.css ├── panel.html └── panel.js ├── resources ├── 16x16.svg ├── 32x32.svg ├── ui-base.css └── ui-color.css ├── screenshots ├── add-bookmark.png ├── add-menu.png └── main.png └── tools └── eslint └── for-module.js /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI/CD 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: prepare manifest.json with a revision number 13 | run: | 14 | cp webextensions/manifest.json ./ 15 | version=$(cat manifest.json | jq -r ".version" | sed -r -e "s/$/.$(git log --oneline | wc -l)/") 16 | cat manifest.json | jq ".version |= \"$version\"" > webextensions/manifest.json 17 | - name: build xpi 18 | run: make 19 | - uses: actions/upload-artifact@v4 20 | with: 21 | name: secondsearch-we.xpi 22 | path: secondsearch-we.xpi 23 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "makexpi"] 2 | path = makexpi 3 | url = https://github.com/piroor/makexpi.git 4 | [submodule "submodules/webextensions-lib-configs"] 5 | path = submodules/webextensions-lib-configs 6 | url = https://github.com/piroor/webextensions-lib-configs.git 7 | [submodule "submodules/webextensions-lib-l10n"] 8 | path = submodules/webextensions-lib-l10n 9 | url = https://github.com/piroor/webextensions-lib-l10n.git 10 | [submodule "submodules/webextensions-lib-options"] 11 | path = submodules/webextensions-lib-options 12 | url = https://github.com/piroor/webextensions-lib-options.git 13 | [submodule "submodules/webextensions-lib-shortcut-customize-ui"] 14 | path = submodules/webextensions-lib-shortcut-customize-ui 15 | url = https://github.com/piroor/webextensions-lib-shortcut-customize-ui.git 16 | [submodule "submodules/webextensions-lib-scroll"] 17 | path = submodules/webextensions-lib-scroll 18 | url = https://github.com/piroor/webextensions-lib-scroll.git 19 | -------------------------------------------------------------------------------- /COPYING.txt: -------------------------------------------------------------------------------- 1 | Each file under this directory is licensed under MPL 2.0 by default, if the file includes no license information. 2 | All rights reserved. 3 | 4 | ---- 5 | 6 | This project includes files licensed under different type licenses: 7 | 8 | * Mozilla Public License 2.0 (MPL 2.0) 9 | * MIT License 10 | 11 | Please note that these licenses are applied "per-file". In most cases each file includes a license header in itself. If a file has no license header due to some reasons (for example, image files), please see a file named "license.txt" in closest parent directory. Otherwise please treat the file is licensed under the default license declared at the top of this file. 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME = secondsearch 2 | 3 | .PHONY: all xpi install_hook lint format 4 | 5 | all: xpi 6 | 7 | xpi: 8 | cd webextensions && $(MAKE) 9 | cp webextensions/$(PACKAGE_NAME)*.xpi ./ 10 | 11 | install_hook: 12 | echo '#!/bin/sh\nmake lint' > "$(CURDIR)/.git/hooks/pre-commit" && chmod +x "$(CURDIR)/.git/hooks/pre-commit" 13 | 14 | lint: 15 | cd webextensions && $(MAKE) $@ 16 | 17 | format: 18 | cd webextensions && $(MAKE) $@ 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Second Search 2 | 3 | ![Build Status](https://github.com/piroor/secondsearch/actions/workflows/main.yml/badge.svg?branch=trunk) 4 | 5 | * [Signed package on AMO](https://addons.mozilla.org/firefox/addon/second-search/) 6 | * [Development builds for each commit are available at "Artifacts" of the CI/CD action](https://github.com/piroor/secondsearch/actions?query=workflow%3ACI%2FCD) 7 | 8 | ## Privacy Policy 9 | 10 | This software does not collect any privacy data automatically, but this includes ability to synchronize options across multiple devices automatically via Firefox Sync. 11 | Any data you input to options may be sent to Mozilla's Sync server, if you configure Firefox to activate Firefox Sync. 12 | 13 | このソフトウェアはいかなるプライバシー情報も自動的に収集しませんが、Firefox Syncを介して自動的に設定情報をデバイス間で同期する機能を含みます。 14 | Firefox Syncを有効化している場合、設定画面に入力されたデータは、Mozillaが運用するSyncサーバーに送信される場合があります。 15 | 16 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piroor/secondsearch/ffb2349ffaac5a6c6f8b2571866f8048959b7579/TODO.txt -------------------------------------------------------------------------------- /history.en.md: -------------------------------------------------------------------------------- 1 | # History 2 | 3 | - master/HEAD 4 | - 2.3.9 (2023.3.12) 5 | * Search in new tabs more certainly. 6 | - 2.3.8 (2022.9.21) 7 | * Apply theme color to the toolbar icon more certainly. 8 | - 2.3.7 (2022.9.15) 9 | * Apply theme color to the toolbar button icon. 10 | - 2.3.6 (2022.7.21) 11 | * Add an option to grant cross origin access to arbitrary host as a workaround about unfetchable favicons. 12 | - 2.3.5 (2022.7.21) 13 | * Give more host permission to fetch favicons. 14 | * Expose some options for default search engine as regular options. 15 | * Hide deactivated options for default search engines. 16 | - 2.3.4 (2021.11.30) 17 | * Open search result tabs more certainly with native saerch engines. 18 | * Add debug options UI to change delay for new tabs and new windows. 19 | * Fix wrong behaviors of "All Configs" UI: apply imported configs to options UI immediately and treat decimal values as valid for some numeric options. 20 | - 2.3.3 (2020.9.11) 21 | * Better visibility on the high contrast mode. 22 | * Reduce animation effect if it is reduced by the platform setting. 23 | - 2.3.2 (2020.8.26) 24 | * Fix initialization error on the first startup. 25 | - 2.3.1 (2020.7.29) 26 | * Define default keyboard shortcut statically. 27 | - 2.3.0 (2019.12.27) 28 | * Support Dark mode of the platform. 29 | * Better coloring with Photon and Dark color scheme. 30 | * Clear cache for keywords correctly. 31 | - 2.2.4 (2019.11.5) 32 | * Add a button to the options page, to refresh the list of search engines from bookmark keywords. 33 | * Show the list of search engines sorted by name with correct width (full width to fill the panel). 34 | * Show scrollbar with narrow width. 35 | - 2.2.3 (2019.6.19) 36 | * Cache favicons of search engines. 37 | * Add ability to export and import all configurations except keyboard shortcuts. (Options => "Development" => "Debug mode" => "All Configs" => "Import/Export") 38 | - 2.2.2 (2018.10.19) 39 | * Apply theme color to toolbar button icon only when it is intentionally activated with `svg.context-properties.content.enabled` = `true`. 40 | - 2.2.1 (2018.10.18) 41 | * Match color of toolbar button icon to the current theme on Firefox 62 and later. 42 | - 2.2.0 (2018.10.17) 43 | * Support Firefox's search engines (on Firefox 63 and later.) 44 | * The keyboard shortcut is now deassignable by hitting the Escape key on its input field. 45 | * Drop support for Firefox 59 and older. 46 | - 2.1.9 (2018.5.30) 47 | * Add ability to switch color scheme to Dark theme. 48 | * Encode search terms before send. 49 | - 2.1.8 (2018.5.15) 50 | * Ignore keyboard events while composition via IME. 51 | - 2.1.7 (2018.5.14) 52 | * Handle keyrepeat of arrow keys to select search engines. 53 | - 2.1.6 (2018.3.7) 54 | * Clear focus of search engine by hovering on the search field. This makes it easy to search pasted text by the default search engine. 55 | * Restore last input of the search field when a search action is aborted and the panel is reopened. 56 | - 2.1.5 (2018.2.11) 57 | * Allow to unfocus from all search engines by keyboard operation. 58 | * Keyboard shortcut is now customizable on Firefox 60 and later. 59 | - 2.1.4 (2018.2.9) 60 | * Synchronize configurations via Firefox Sync. 61 | * Reformat keys of localized messages matching to the [spec](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/i18n/Locale-Specific_Message_reference#Member_details). 62 | - 2.1.3 (2018.2.8) 63 | * Clear focus on search engine by starting input. 64 | * Clear last search term with delay. 65 | - 2.1.2 (2017.11.30) 66 | * Search results for actions with accel key (Ctrl or ⌘) is now configurable where to be opened in. 67 | * When the search field is filled by selected text in the current tab, the "Search" button is automatically shown like for pasted text. 68 | * Selection text in any input field is now detected correctly. 69 | * Add an option to deactivate "auto fill with selection text" behavior. 70 | - 2.1.1 (2017.11.27) 71 | * Expand search field to fill the width of the popup. (regression) 72 | * Use white icon for the toolbar button on the "Dark" theme. 73 | * Recycle `about:privatebrowsing` tab as a blank tab on a private browsing window. 74 | - 2.1.0 (2017.11.24) 75 | * Add margin around UI to match Photon design. 76 | * Search with Google by default correctly. 77 | * "Search" button is available after copy and paste. 78 | * Add ability to keep popup open after search. Moreover, if you do search with Ctrl or Command key, the popup is always kept open. 79 | - 2.0.1 (2017.11.21) 80 | * Use background and foreground color same to Photon. This is required to avoid ghost rendering with missing background color on some environment. (I confirmed on Windows 10.) 81 | - 2.0.0 (2017.11.21) 82 | * Rebuilt on WebExtensions, as a custom search field addon for bookmarks with keyword. 83 | - 1.0.2016020401 84 | * Works correctly for search engines with some missing information (ex. favicon). 85 | - 1.0.2016020301 86 | * Works correctly on clean environment. 87 | - 1.0.2016012701 88 | * Don't detect newly opened tabs as search result tabs, after a search with virtual search engines (form keyworded bookmarks). 89 | - 1.0.2016012601 90 | * Better compatibility with recent Tree Style Tab. 91 | * Don't detect newly opened tabs as search result tabs correctly, when any unknown exception raised while doing search. 92 | - 1.0.2015122401 93 | * Drop support for Firefox 37 and older versions. 94 | * Works correctly even if there is only the location bar with activated "UnifiedComplete" feautre on Firefox 43 and later. 95 | * The default search engine with POST method works correctly. 96 | * Save chosen engine to the list of recently used engines for one-off search. 97 | * Made synchronous API free around the Places SQLite database. 98 | * The cache of bookmarks with smart keywords are now saved into a JSON file instead of a preference. 99 | * Detect smart keywords of bookmarks correctly on Firefox 39 and later. 100 | - 0.9.2015020901 101 | * Move focus to Second Search's popup menu by the up arrow key correctly, on Linux. (regression) 102 | - 0.9.2015020801 103 | * Made `eval()`-free. 104 | * Better compatibility with new Web search UI on Firefox 35 (and later). 105 | - 0.8.2014120202 106 | * Search by the default engine works correctly. 107 | (On Firefox 33, unexpected engine was used, and Firefox 34 couldn't do search.) 108 | - 0.8.2014120201 109 | * Works on Nightly 37.0a1. 110 | * Drop support for Firefox 30 and older versions 111 | * Works correctly on the multi-process mode (E10S). 112 | * Most implementations are migrated to JavaScript code modules. 113 | * Works correctly on Firefox 34 and later (after [bug 1088660: the "one for search" feature](https://bugzilla.mozilla.org/show_bug.cgi?id=1088660).) 114 | * Better compatibility with the [Private Tab](https://addons.mozilla.org/firefox/addon/private-tab/) addon. 115 | - 0.7.2014050501 116 | * Works correctly after overflow/underflow the toolbar, on Australis. 117 | * Works on both location bar and search bar if one of them is in the menu panel, on Australis. 118 | - 0.7.2014050201 119 | * Works on Nightly 32.0a1. 120 | * Modified: "jar" archive is no longer included. 121 | - 0.7.2012122901 122 | * Works on Nightly 20.0a1. 123 | * Fixed: Clear the search term after search correctly, when it is searched by an engine in the popup. 124 | * Modified: Clear the search term after search by default. (You can disable this feature by the configuration dialog.) 125 | - 0.7.2012101401 126 | * Fixed: Now works at the location bar correctly on Firefox 17 and later. 127 | * Fixed: Search the typed term in the location bar even if "browser.urlbar.autoFill" is "true". 128 | - 0.7.2012031101 129 | * Update for Firefox 13.0a1. 130 | * Drop support for Firefox older than 10. 131 | * Drop support for Thunderbird. 132 | * Fixed: Show the caret in the search bar during the list of search engines is shown. 133 | * Fixed: Scroll the list of search engines automatically during dragging on scroll buttons in the popup. 134 | * Improved: Make compatible with [Tab Utilities](https://addons.mozilla.org/firefox/addon/tab-utilities/). 135 | * Improved: Make compatible with [Searchbar Autosizer](https://addons.mozilla.org/firefox/addon/searchbar-autosizer/). 136 | * Improved: Make compatible with [Tab Control](https://addons.mozilla.org/firefox/addon/tab-control/). 137 | * Improved: Make compatible with [SearchLoad Options](https://addons.mozilla.org/firefox/addon/searchload-options/). 138 | * Add Swedish locale (translated by Mikael Hiort af Ornäs) 139 | - 0.6.2011052802 140 | * Improved: Left-click and middle-click on the magnifier button in the search bar now work as you customized in Second Search configuration. 141 | - 0.6.2011052801 142 | * Fixed: "Reuse blank tab" option didn't work for search results from the default engine. 143 | * Fixed: The context menu on the content area was unexpectedly blocked. 144 | - 0.6.2011051101 145 | * Fixed: Alt key didn't invert the behavior for the focus of opened tab (foreground/background), about search results from specified engine. 146 | * Modified: The default behavior of opened tabs is now initialized based on the preference "browser.tabs.loadInBackground" (for tabs opened by "target" attribute), instead of "browser.tabs.loadInBackground" (for tabs opened by middle click). If you want to change the current behavior directly, go to "about:config" and change the value of the preference "secondsearch.loadInBackground". 147 | - 0.6.2010120901 148 | * Works on Minefield 4.0b8pre. 149 | * Drop support for Firefox 3.0 and older versions. 150 | - 0.5.2009091201 151 | * Fixed: Odd behavior of a radio button in the configuration dialog disappeared. 152 | * Fixed: Search results are correctly loaded into new tab even if Tab Mix Plus is installed. 153 | - 0.5.2009050801 154 | * Fixed: Works correctly even if there is engine which has no favicon. 155 | * Fixed: Bookmarklets ("javascript:" bookmarks) are always loaded into the current tab. 156 | * Some internal operations are brushed up. 157 | - 0.5.2008111401 158 | * Fixed: It works correctly after you do "search" when the popup was going to be showing. 159 | * Modified: "by ..." disappeared from the popup. 160 | * Updated: hu-HU locale is updated by Mikes Kaszmán István. 161 | - 0.5.2008101401 162 | * Improved: Shift-Up/Down keys shows the Second Search popup, so, you can use Up/Down keys to select autocomplete item. 163 | * Improved: Escape key always closes the Second Search popup. 164 | * Fixed: Popup menu is re-positioned silently. 165 | * Fixed: Dropping text onto the menu item which is same to the current engine works correctly. 166 | * Fixed: Popup is correctly shown while dragging on the engine button even if drag-and-drop operation is allowed in the search box. 167 | * Updated: Works on Minefield 3.1b2pre. 168 | * Updated: Hungarian locale is updated. (by Mikes Kaszmán István) 169 | - 0.5.2008091601 170 | * Fixed: "Choose the engine from popup after dropped" works correctly. 171 | - 0.5.2008091501 172 | * Fixed: Popups work correctly. In the previous version, popups possibly disappeared after searching. 173 | * Fixed: "Allow drag-and-drop of text in the textbox" works correctly. 174 | - 0.5.2008090201 175 | * Fixed: Freezing on finding favicons for smart keywords disappeared. 176 | * Updated: Hungarian locale is updated. (by Mikes Kaszmán István) 177 | - 0.5.2008090101 178 | * Improved: Second Search works on the location bar if there is no search bar. 179 | * Improved: Second Search shows its popup while dragging, for Firefox 3 on Mac OS X. 180 | * Fixed: The cursor doesn't disappear on Firefox 3. 181 | * Fixed: Items for smart keywords are correctly updated. 182 | * Fixed: Conflict with some third-pirty's themes disappeared. 183 | * Firefox 1.5 support dropped. 184 | - 0.4.2008052301 185 | * Fixed: Delayed popups work fine in Firefox 3 on Windows. 186 | - 0.4.2008042801 187 | * Fixed: Works on the latest Trunk. 188 | - 0.4.2008021501 189 | * Fixed: Auto-popup on the search bar while dragging works correctly on Linux. 190 | - 0.4.2008021201 191 | * Improved: Works on Minefield. (Firefox 3 beta3) 192 | * Improved: Works on Thunderbird 2. 193 | * Fixed: Works correctly even if there is search engine without icon. 194 | - 0.3.2007120601 195 | * Improved: Combination with the [Split Browser](http://piro.sakura.ne.jp/xul/_splitbrowser.html.html) is improved. 196 | - 0.3.2007110501 197 | * Fixed: Toolbar customizing works correctly. 198 | - 0.3.2007090301 199 | * Fixed: The search term is cleared correctly after the search is done by an engine from the popup which is shown by text-drop. 200 | * Modified: The default engine is listed in the popup and popuphidden event doesn't start search, for the popup which is shown by text-drop. 201 | - 0.3.2007061801 202 | * Modified: In Mac OS X, the popup cannot be shown when a string is dragged to the search bar. So, the option is disabled permanently in Mac. 203 | * Updated. Hungarian locale is updated. 204 | - 0.3.2007052402 205 | * Imrpvoed: A new option to change the delay to clear search bar is available. 206 | * Improved: The search term isn't cleared if it is changed after search. 207 | * Modified: The delay to clear search bar is changed. 208 | * Modified: Configuration dialog is restructured. 209 | - 0.3.2007052401 210 | * Fixed: Text in the search bar are cleared after a delay. 211 | * Fixed: Correctly searches by the engine selected from the popup even if it has no keyword. 212 | - 0.3.2007052201 213 | * Updated: Hungarian locale is updated. (by Mikes Kaszmán István) 214 | - 0.3.2007052101 215 | * Added: Hungarian locale is available. (by Mikes Kaszmán István) 216 | - 0.3.2007052001 217 | * Improved: Options to open search results in new tabs and focus it immidiately are available. 218 | - 0.3.2007051401 219 | * Modified: Popup for the engine button is shown like as a context menu. 220 | - 0.3.2007051201 221 | * Modified: The list of engines recently used is automatically filled if there are less history. 222 | * Modified: The Japanese name is modified. 223 | - 0.3.2007051101 224 | * Improved: Context menu on the engine button in the search bar becomes the Second Search popup. 225 | * Improved: Each type of popup for the context menu on the engine button or for dorag-and-drop can be customized. 226 | * Improved: New tab opened from the search bar is stay background as the default preference of Firefox 2. 227 | * Fixed: Search term is cleared correctly for the middle-click on the "Search" button. 228 | * Fixed: The popup for text input is shown after the delay correctly. 229 | * Fixed: Infinity multiplying keywords disappeared. 230 | * Modified: The number of the engines recently used is grown. 231 | * Modified: The popup for text input is shown after a delay. 232 | - 0.3.2007050701 233 | * Improved: Popups can be shown with a delay when you input text into the search bar. (Go to "about:config" and set the delay to `secondsearch.popup.auto_show.delay` in milliseconds.) 234 | - 0.3.2007050202 235 | * Fixed: Popup menu for drag-over is shown correctly in Linux. 236 | - 0.3.2007050201 237 | * Improved: For drag-over on the search bar, the popup is shown and you can search by the choosen engine directly with drag-and-drop to the popup item. (Solution for [Bug 274432](https://bugzilla.mozilla.org/show_bug.cgi?id=274432)) 238 | * Improved: A new option to allow drag-and-drop texts in the textbox. With this preference, you can start to search directly by drag-and-drop to the button (engine icon) in the search bar. 239 | - 0.2.2007050201 240 | * Fixed: The last item of the popup can be focused by the up key. 241 | * Fixed: Smart keywords are parsed as search engines correctly. 242 | * Fixed: Rebuilding the cache of smart keywords works correctly. 243 | * Fixed: English locale is corrected. 244 | - 0.2.2007050101 245 | * Fixed: The search bar is cleared automatically after doing search without the popup. 246 | * Fixed: The popup is shown correctly even if there is a popup for the suggest feature. 247 | * Fixed: The name of the current engine is shown correctly after the search bar is cleared automatically. 248 | * Improved: Middle-click on the popup opens the search result to a new tab. 249 | - 0.2.2007032902 250 | * Fixed: Popups are closed automatically after any element is clicked by user. 251 | - 0.2.2007032901 252 | * Improved: A new option to clear search bar after doing search is available. 253 | * Fixed: Popups are closed automatically after the window lost its focus. 254 | - 0.2.2007032401 255 | * Fixed: Popups kept wrongly shown disappeared. (maybe) 256 | - 0.2.2007010201 257 | * Improved: Popup can be shown below the search bar. (but it doesn't work with auto-complete and suggest) 258 | * Improved: Popup can be hidden for drag-and-drop to the search bar. 259 | * Fixed: Initializing operation of smart keywords is optimized for environments which have no keyword. 260 | - 0.2.2006122201 261 | * Improved: Automatic showing of the popup can be disabled. 262 | * Fixed: "Rebuild the list of smart keywords" button works correctly. 263 | * Fixed: Broken recent-used search engines after you select a smart-keyword engine has disappeared. 264 | * Fixed: Icons for smart-keyword items are correctly shown. 265 | - 0.2.2006122102 266 | * Fixed: The size of the configuration dialog made flexible. 267 | - 0.2.2006122101 268 | * Improved: Configuration dialog is available. (for Firefox 1.5 or later) 269 | * Improved: All of recently used engines are shown if the setting of the number of items for recent engines has a nevative value. 270 | * Improved: Smart keywords are available like as search engines. (available only for Firefox builds without Places) 271 | * Improved: All of engines can be shown instead of the recent engines. (pref name "secondsearch.popup.type", value: 0 means "default", 1 is "show all engines", and 2 is "show all engines in reversed order". 272 | - 0.1.2006121901 273 | * Fixed: Wrong position of the popup is corrected. 274 | - 0.1.2006121801 275 | * Improved: When you drop terms to the search bar, Second Search popup will appear. If you select no engine, the term will be searched by the default engine. 276 | - 0.1.2006121602 277 | * Improved: Second Search change the engine of the search bar to the selected engine if there is no term in the bar. (If you want this extension never to change the state of the search bar, change "secondsearch.switch.blank_input" to "false" by "about:config".) 278 | - 0.1.2006121601 279 | * Fixed: Conflict with Tab Mix Plus on Firefox 2 disappeared. 280 | - 0.1.2006121502 281 | * Fixed: Uninstalled engines disappeared from the recent engines correctly. 282 | * Improved: Recent used engines disappeared from the "Search by" submenu. 283 | - 0.1.2006121501 284 | * Released. 285 | -------------------------------------------------------------------------------- /history.ja.md: -------------------------------------------------------------------------------- 1 | # 更新履歴 2 | 3 | - master/HEAD 4 | - 2.3.9 (2023.3.12) 5 | * 新しいタブでの検索結果をより確実に開くようにした 6 | - 2.3.8 (2022.9.21) 7 | * ツールバーボタンにより適切にテーマの配色を反映するようにした 8 | - 2.3.7 (2022.9.15) 9 | * ツールバーボタンのアイコンにテーマの配色を反映するようにした 10 | - 2.3.6 (2022.7.21) 11 | * favicon取得に失敗する場合の回避策として、任意のホストへのクロスオリジンアクセスを許可するオプションを追加 12 | - 2.3.5 (2022.7.21) 13 | * favicon取得のためのWebサイトへのアクセス件を追加 14 | * 既定の検索エンジンでの検索に関する詳細動作のオプションを通常の設定項目の中に含めるようにした 15 | * 既定の検索エンジンでの検索に関する無効化された設定項目は非表示にするようにした 16 | - 2.3.4 (2021.11.30) 17 | * ネイティブの検索エンジンの検索結果をタブで開く動作をより確実に動作するようにした 18 | * 新しいタブや新しいウィンドウで検索結果を平草委の遅延時間を変更するデバッグ用オプションUIを追加 19 | * 「すべての設定」のUIの不備を改善:インポートした設定をUIに即座に反映し、また、数値型の一部の設定項目で小数が不正な値として警告されてしまわないようにした 20 | - 2.3.3 (2020.9.11) 21 | * ハイコントラストモードでの見やすさを向上 22 | * プラットフォームの設定でアニメーション効果が無効のときはアニメーション効果を減らすようにした 23 | - 2.3.2 (2020.8.26) 24 | * 初回使用時の初期化エラーを解消 25 | - 2.3.1 (2020.7.29) 26 | * 既定のキーボードショートカットを静的に定義するようにした 27 | - 2.3.0 (2019.12.27) 28 | * プラットフォームのダークモードに対応 29 | * Photonおよびダークモードの配色をより見栄えよく反映するようにした 30 | * キーワードのキャッシュを正しく消去するようにした 31 | - 2.2.4 (2019.11.5) 32 | * ブックマークキーワードからの検索エンジンの一覧を更新するためのボタンを設定画面に追加した 33 | * 名前順の検索エンジン一覧がパネルの幅いっぱいの幅で表示されない場合があったのを修正 34 | * スクロールバーの表示幅を細くした 35 | - 2.2.3 (2019.6.19) 36 | * 検索エンジンのアイコンをキャッシュするようにした 37 | * キーボードショートカットを除く全設定のインポートとエクスポートに対応(設定→開発用→デバッグモード→すべての設定→Import/Export) 38 | - 2.2.2 (2018.10.19) 39 | * `svg.context-properties.content.enabled`が`true`に設定されていて意図的に有効化された場合にのみ、テーマの配色をツールバーのアイコンに反映するようにした 40 | - 2.2.1 (2018.10.18) 41 | * Firefox 62以降において、ツールバーボタンの色をテーマの配色に合わせるようにした 42 | - 2.2.0 (2018.10.17) 43 | * Firefox自体の検索エンジンに対応(Firefox 63以降) 44 | * キーボードショートカットの入力欄でのEscapeキー押下でショートカットの割り当てを解除できるようにした 45 | * Firefox 59およびそれ以前のバージョンへの対応を終了 46 | - 2.1.9 (2018.5.30) 47 | * Darkテーマ用の配色に変えられるようにした 48 | * 検索語句をURLエンコードしてから送信するようにした 49 | - 2.1.8 (2018.5.15) 50 | * IMEによる変換操作中のキー操作を無視するようにした 51 | - 2.1.7 (2018.5.14) 52 | * 検索エンジン選択時のキー操作でキーリピートを受け付けるようにした 53 | - 2.1.6 (2018.3.7) 54 | * 入力欄に語句をペーストしたときなどに既定の検索エンジンで検索しやすいように、入力欄のhoverで検索エンジンの選択状態をクリアするようにした 55 | * 検索操作を中断した後で再度パネルを開いた場合にも、入力欄の内容を復元するようにした 56 | - 2.1.5 (2018.2.11) 57 | * キーボード操作ですべての検索エンジンからフォーカスを外せるように↓ 58 | * Firefox 60以降においてキーボードショートカットをカスタマイズできるようにした 59 | - 2.1.4 (2018.2.9) 60 | * Firefox Syncで設定を同期するようにした 61 | * 言語リソースのキー名が[仕様](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/i18n/Locale-Specific_Message_reference#Member_details)に則っていなかったのを修正 62 | - 2.1.3 (2018.2.8) 63 | * 入力操作の開始時に検索エンジンのフォーカス状態をリセットするようにした 64 | * 検索実行後に検索語句を消去するまでの待ち時間を設けた 65 | - 2.1.2 (2017.11.30) 66 | * アクセルキー(Ctrlまたは⌘)を伴う操作で検索を実行した際の結果をどのように開くかを設定可能にした 67 | * ページ内の選択文字列を検索語句として自動入力した場合に、文字列を貼り付けた時と同様に「検索」ボタンを表示するようにした 68 | * ページ内の入力欄の中で選択された文字列を認識できるようにした 69 | * ページ内の選択文字列を検索語句として自動入力する動作を設定で無効化できるようにした 70 | - 2.1.1 (2017.11.27) 71 | * 検索窓がポップアップの幅一杯に広がっていなかったのを修正(後退バグ) 72 | * 「Dark」テーマではツールバーのボタンに白いアイコンを使うようにした 73 | * プライベートブラウジングウィンドウにおいて、`about:privatebrowsing`のタブを空のタブとして再利用するようにした 74 | - 2.1.0 (2017.11.24) 75 | * Photonデザインに合うよう、UIの周囲にマージンを設けた 76 | * 既定の検索エンジンとしてGoogleでの検索を正しく行えるようにした 77 | * コピー&ペースト後に「検索」ボタンを表示するようにした 78 | * 検索後もポップアップを開いたままにできるようにした(CtrlまたはCommandキーを押して検索した場合は常にポップアップが開かれたままになります) 79 | - 2.0.1 (2017.11.21) 80 | * Photonと同色の背景色と文字色を使うようにした(背景色の指定が無い場合に文字のゴーストが表示されてしまうのを防ぐため。作者はWindows 10環境でこの問題を視認しました。) 81 | - 2.0.0 (2017.11.21) 82 | * WebExtensionsベースで、キーワードを伴うブックマークでの検索専用の検索窓を提供するアドオンとして作り直した 83 | - 1.0.2016020401 84 | * faviconなどいくつかの情報が足りない検索エンジンがある場合でも正しく動作するようにした 85 | - 1.0.2016020301 86 | * まっさらの環境で動作しなくなっていなかったのを修正 87 | - 1.0.2016012701 88 | * (キーワード付きのブックマークに基づく)仮想的な検索エンジンで検索を実行した後に、それ以後開かれるタブが常に検索結果のタブとして扱われてしまう問題を修正 89 | - 1.0.2016012601 90 | * 最近のツリー型タブとの互換性を向上 91 | * 検索の途中で未知の例外が発生した時に、それ以後開かれるタブが常に検索結果のタブとして扱われてしまう問題を修正 92 | - 1.0.2015122401 93 | * Firefox 37およびそれ以前のバージョンへのサポートを終了 94 | * Firefox 43以降でロケーションバーのみがツールバー上にあり且つ「UnifiedComplete」機能が有効な時に、キーボード操作でセカンドサーチのポップアップにフォーカスを移せなかったのを修正 95 | * 既定の検索エンジンがPOSTメソッドを使用する物であっても正しく動作するようにした 96 | * One-Off Searchで使われた検索エンジンを最近使われた検索エンジンの一覧に反映するようにした 97 | * PlacesのSQLiteデータベースに関する処理などで同期APIを使わないようにした 98 | * スマートキーワード付きブックマークのキャッシュを、設定値ではなくJSONファイルとして保存するようにした 99 | * Firefox 39以降のバージョンでスマートキーワード付きのブックマークを正しく収集するように修正 100 | - 0.9.2015020901 101 | * Linuxにおいて上キーでセカンドサーチのポップアップメニューに正しくフォーカスを移すようにした(リグレッション修正) 102 | - 0.9.2015020801 103 | * `eval()`を使用しない設計に改めた 104 | * Firefox 35以降の新しいWeb検索バーの挙動との整合性を向上 105 | - 0.8.2014120202 106 | * 普通の検索を実行した時の結果がおかしくなっていた(Firefox 33では別の検索エンジンが使われてしまい、Firefox 34以降ではそもそも検索が実行されない)のを修正 107 | - 0.8.2014120201 108 | * Nightly 37.0a1に対応 109 | * Firefox 30およびそれ以前のバージョンへの対応を終了 110 | * マルチプロセスモード(E10S)に対応 111 | * 実装の大部分をJavaScriptコードモジュール化した 112 | * Firefox 34以降で動作するよう修正([bug 1088660「one for search」](https://bugzilla.mozilla.org/show_bug.cgi?id=1088660)が導入されて以降のバージョンに対応) 113 | * [Private Tab](https://addons.mozilla.org/firefox/addon/private-tab/)との互換性を向上 114 | - 0.7.2014050501 115 | * Australisにおいて、ツールバーのオーバーフロー・アンダーフローが発生した後でも正常に動作するようにした 116 | * Australisにおいて、ロケーションバーと検索バーのどちらかがメニューパネルの中にある場合はどちらでも動作するようにした 117 | - 0.7.2014050201 118 | * Nightly 32.0a1に対応 119 | * jarファイルを含めない形のパッケージングに変更 120 | - 0.7.2012122901 121 | * Nightly 20.0a1に対応 122 | * ポップアップから選択した検索エンジンで検索した後に検索語句が自動的に消去されない問題を修正 123 | * 検索の実行後に検索語句を自動的に消去する設定を既定の状態で有効にした 124 | - 0.7.2012101401 125 | * Firefox 17以降のロケーションバーとの組み合わせで動作しなくなっていたのを修正 126 | * 「browser.urlbar.autoFill」がtrueの時にロケーションバーからの検索ができなかったのを修正 127 | - 0.7.2012031101 128 | * Firefox 13.0a1に対応 129 | * Firefox 10よりも古いバージョンへの対応を終了 130 | * Thunderbirdへの対応を終了 131 | * 検索エンジンの一覧が表示されている間検索バー内のキャレットが表示されなくなる問題を修正 132 | * ドラッグ操作中に検索エンジンの一覧のスクロールボタン上で待つと検索エンジンの一覧を自動的にスクロールするようにした 133 | * [Tab Utilities](https://addons.mozilla.org/firefox/addon/tab-utilities/)との併用に対応 134 | * [Searchbar Autosizer](https://addons.mozilla.org/firefox/addon/searchbar-autosizer/)との併用に対応 135 | * [Tab Control](https://addons.mozilla.org/firefox/addon/tab-control/)との併用に対応 136 | * [SearchLoad Options](https://addons.mozilla.org/firefox/addon/searchload-options/)との併用に対応 137 | * スウェーデン語ロケール追加(translated by Mikael Hiort af Ornäs) 138 | - 0.6.2011052802 139 | * 検索バー内の虫眼鏡ボタン上で左クリックまたは中クリックした場合に、セカンドサーチの設定に従って動作するようにした 140 | - 0.6.2011052801 141 | * 「空のタブを再利用する」機能が既定の検索エンジンでの検索に対して機能していなかったのを修正 142 | * コンテンツ領域のコンテキストメニューが開けなくなる事があったのを修正 143 | - 0.6.2011051101 144 | * 検索エンジンを上下キーで選択して確定する際に、Altキーの押下によって現在のタブで開くか新しいタブで開くかの挙動が反転されるべきであるにも関わらず、挙動が反転されていなかったのを修正 145 | * 検索結果のタブをバックグラウンドで開くかどうかの既定の挙動について、リンクのミドルクリック時に適用される設定 browser.tabs.loadInBackground ではなく、target属性を指定されたリンクなどを開く時に適用される設定 browser.tabs.loadDivertedInBackground に基づいて設定を初期化するようにした(検索ボックスからAlt-Enterでタブを開く時にバックグラウンドのタブで開くかどうかの設定を直接編集するには、about:configでsecondsearch.loadInBackgroundの値を変更して下さい) 146 | - 0.6.2010120901 147 | * Minefield 4.0b8pre対応 148 | * Firefox 3.0およびそれ以前のバージョンに対するサポートを終了 149 | - 0.5.2009091201 150 | * 検索結果の表示先のラジオボタンについて、「新しいタブに読み込み、すぐにそのタブに切り替える」を選択するとチェックが「新しいタブに読み込む」の方に移ってしまう問題を修正 151 | * Tab Mix Plusがインストールされている環境で検索結果が常に現在のタブで読み込まれる問題に対処(Tab Mix Plusの仕様変更に追従) 152 | - 0.5.2009050801 153 | * faviconが無い検索エンジンがあると正常に動作しない問題を修正 154 | * ブックマークレットの場合は常に現在のタブに読み込むようにした 155 | * 内部処理を若干ブラッシュアップ 156 | - 0.5.2008111401 157 | * 入力などの操作によってポップアップが開かれる場面で、ポップアップが開かれる前に検索を実行した場合に、それ以後ポッパップが開かれなくなる問題を修正 158 | * 検索エンジンのリストで「~で検索」を表示せず検索エンジン名だけを示すようにした 159 | * ハンガリー語ロケール更新(by Mikes Kaszmán István) 160 | - 0.5.2008101401 161 | * Shift-上下キーで常にポップアップを表示する機能を追加し、上下キーによるオートコンプリートとの使い分けを可能にした。 162 | * Escapeキーで常にポップアップを閉じるようにした。 163 | * ポップアップの表示位置を調整する時の途中の状態を表示しないようにした。 164 | * 文字列をドラッグして、現在の検索エンジンに該当するメニュー項目にドロップした時、検索が実行されない問題を修正。 165 | * 検索ボックス内での文字列のドラッグ&ドロップを許可している時に、検索エンジン切り替えボタンへのドラッグでポップアップが表示されない問題を修正。 166 | * Minefield 3.1b2preでの動作を確認。 167 | * ハンガリー語ロケール更新 168 | - 0.5.2008091601 169 | * 文字列のドロップ後に検索エンジンを選択する機能が働いていなかったのを修正 170 | - 0.5.2008091501 171 | * 検索を実行した後、ポップアップが表示されなくなる事がある問題を修正 172 | * 検索バー内での文字列のドラッグ&ドロップを許容する設定が機能しなくなっていたのを修正 173 | * ハンガリー語ロケール更新 174 | - 0.5.2008090201 175 | * スマートキーワードの取得時に固まることがある問題を修正 176 | - 0.5.2008090101 177 | * 検索バーが表示されていない場合は、ロケーションバーで検索を行えるようにした 178 | * Mac OS XでもFirefox 3ではドラッグ中にポップアップを出すようにした 179 | * Firefox 3で、検索バーに入力中にポップアップが表示されるとカーソルが消える問題を修正 180 | * スマートキーワードに対応する項目の自動更新に失敗することがあったのを修正 181 | * テーマによってポップアップが出ない事がある問題を修正 182 | * Firefox 1.5のサポートを終了 183 | - 0.4.2008052301 184 | * Firefox 3 on Windowsで文字列のドラッグ&ドロップ時にポップアップがすぐ消えてしまう問題を修正 185 | - 0.4.2008042801 186 | * Trunkの仕様変更に追従 187 | - 0.4.2008021501 188 | * Linuxで文字列のドラッグ中のポップアップメニューの自動表示が機能しなくなっていたのを修正 189 | - 0.4.2008021201 190 | * Minefield(Firefox 3 beta3)対応 191 | * Thunderbird 2対応 192 | * アイコンのない検索エンジンがあるとエラーになる問題を修正 193 | - 0.3.2007120601 194 | * [分割ブラウザ](http://piro.sakura.ne.jp/xul/_splitbrowser.html)との連携を強化 195 | - 0.3.2007110501 196 | * 他のアドオンと衝突してツールバーのカスタマイズができなくなることがあったのを修正 197 | - 0.3.2007090301 198 | * 文字列のドロップ後にポップアップを表示する設定の時、検索した後に検索バーの内容が消去されない問題を修正 199 | * 文字列のドロップ後にポップアップを表示する設定の時、ポップアップ内の項目にデフォルトの検索エンジンを表示して、ポップアップ以外をクリックした時には操作をキャンセルするようにした 200 | - 0.3.2007061801 201 | * Mac環境では設定パネルにおいて「ドラッグオーバー時にポップアップ表示」を選択できないようにした 202 | * ハンガリー語ロケール更新 203 | - 0.3.2007052402 204 | * 検索バーの内容をクリアするまでの待ち時間を設定するUIを加えた 205 | * 検索実行後に検索バーの内容をクリアする設定の時、クリアするまでに入力内容が変更された場合はクリアしないようにした 206 | * 検索バーの内容をクリアするまでの初期状態の待ち時間を変更 207 | * タブ関係の設定UIを整理した 208 | - 0.3.2007052401 209 | * 検索バーを自動でクリアする機能について、常に一定時間処理を遅らせるようにした 210 | * 検索に失敗する場合があったのを修正 211 | - 0.3.2007052201 212 | * ハンガリー語ロケール更新(by Mikes Kaszmán István) 213 | - 0.3.2007052101 214 | * ハンガリー語ロケールを追加(by Mikes Kaszmán István) 215 | - 0.3.2007052001 216 | * 検索結果を常にタブで開く設定、検索結果をタブで開く時にフォーカスするかどうかの設定項目を加えた 217 | - 0.3.2007051401 218 | * ボタンの上でコンテキストメニューを開いた時のポップアップを、ポインタの位置に表示するようにした 219 | - 0.3.2007051201 220 | * 最近使った検索エンジンの履歴の数が設定より少ない場合、足りない分をすべての検索エンジンの一覧から取得して埋めるようにした 221 | * 日本語の拡張機能名をカタカナで表示するようにした 222 | - 0.3.2007051101 223 | * 検索エンジン切り替えボタンの上でコンテキストメニューを開いた時にSecond Searchのポップアップを表示するようにした 224 | * ポップアップの種類について、エンジン切り替えボタンの上でのコンテキストメニュー、ドラッグドロップ時に、それぞれ別の種類を設定できるようにした 225 | * 検索結果を新しいタブで開く際、タブをアクティブにするかどうかについてFirefox標準の設定を参照するようにした 226 | * 検索バーの検索実行ボタンを中クリックした時に検索バーの内容がクリアされない問題を修正 227 | * ポップアップを表示するまでの遅延時間を設定しても意図通りに動いていなかったのを修正 228 | * キーワードの項目が無限増殖する問題を修正 229 | * デフォルトで表示される検索エンジンの履歴の数を増やした 230 | * デフォルトでキー入力時の自動ポップアップ表示までに遅延を設けた 231 | - 0.3.2007050701 232 | * キー入力時に自動でポップアップ表示するまでの待ち時間を設定できるようにした(about:configで `secondsearch.popup.auto_show.delay` に遅延時間をミリ秒単位で指定してください) 233 | - 0.3.2007050202 234 | * ドラッグ中にポップアップを表示する機能がLinuxで動いていなかったのを修正 235 | - 0.3.2007050201 236 | * ドラッグ&ドロップ時の挙動について、ドラッグ中にポップアップを表示してエンジンを選択できるようにした(ポップアップの項目へのドロップで検索を実行,[Bug 274432](https://bugzilla.mozilla.org/show_bug.cgi?id=274432)への独自の対応) 237 | * 入力欄内でのテキストのドラッグ&ドロップを許可する(ボタン部分へのドロップのみに反応させる)機能を加えた 238 | - 0.2.2007050201 239 | * ポップアップを下に表示する設定の時、↑キーで一番下の項目にフォーカスできなかったのを修正 240 | * スマートキーワードを検索エンジンとして正しく扱えなくなっていたのを修正 241 | * スマートキーワード一覧を取得し直す処理が意図通りに働いていなかったのを修正 242 | * 英語ロケールを修正 243 | - 0.2.2007050101 244 | * 通常の検索時に検索バーの内容が自動でクリアされない問題を修正 245 | * 手動でポップアップを表示する設定の時、サジェスト機能の候補が表示されているとポップアップが表示されない問題を修正 246 | * 検索バーを自動でクリアした後、検索エンジン名が表示されない問題を修正 247 | * ポップアップの項目の中クリックで、新規タブで検索できるようにした 248 | - 0.2.2007032902 249 | * フォーカスできない箇所をクリックした時にもポップアップを閉じるようにした 250 | - 0.2.2007032901 251 | * 検索の実行後に検索バーの内容を空にする機能を加えた 252 | * ウィンドウがフォーカスを失った時もポップアップを閉じるようにした 253 | - 0.2.2007032401 254 | * 他の部分にフォーカスを移した時にポップアップが表示されっぱなしになる現象について改善したつもり 255 | - 0.2.2007010201 256 | * ポップアップを検索バーの下に表示できるようにした(ただし、下に置いた場合はオートコンプリートと併用不可能) 257 | * 文字列のドロップ時にポップアップを表示するかどうかを設定で制御できるようにした 258 | * スマートキーワードを利用していない場合に、ウィンドウを開くごとに何度もスマートキーワードの抽出処理が実行されていたのを修正 259 | - 0.2.2006122201 260 | * ポップアップを自動的に表示するかどうかの設定を加えた 261 | * スマートキーワードの一覧を再取得するボタンが正常に機能していなかったのを修正 262 | * スマートキーワードの選択後などに最近選択した検索エンジンの一覧が壊れることがあったのを修正 263 | * スマートキーワードのアイコンが表示されない問題を修正 264 | - 0.2.2006122102 265 | * 設定ダイアログの大きさを固定しないようにした 266 | - 0.2.2006122101 267 | * 設定ダイアログを加えた(Firefox 1.5以降のみ) 268 | * 検索エンジンの表示数の設定値が負の場合、最近利用したすべての検索エンジンを表示するようにした 269 | * スマートキーワードも検索エンジンの一覧に列挙するようにした(非Placesビルドのみ) 270 | * 最近使った検索エンジンではなく、すべての検索エンジンを常に表示する機能を加えた(設定名「secondsearch.popup.type」、値:0(標準)、1(すべてのエンジンを表示)、2(逆順ですべてのエンジンを表示)) 271 | - 0.1.2006121901 272 | * ポップアップの位置がおかしかったのを修正したつもり 273 | - 0.1.2006121801 274 | * 検索バーに選択文字列をドラッグ&ドロップした時にもSecond Searchのポップアップを表示するようにした(何も選択しなければデフォルトの動作になります) 275 | - 0.1.2006121602 276 | * 検索語句が入力されていない状態でSecond Searchのポップアップで検索エンジンを選択したときは、検索バーのエンジンをそちらに切り替えるようにした(「Second Search上での操作ではいかなる場合でも検索バーの状態を変化させたくない」という場合は、about:configで「secondsearch.switch.blank_input」を「false」に設定してください) 277 | - 0.1.2006121601 278 | * Firefox 2 + Tab Mix Plusの環境でのコンフリクトを解消 279 | - 0.1.2006121502 280 | * 削除した検索エンジンが最近使ったエンジン一覧に残ってしまっていたのを修正 281 | * 最近使ったエンジン一覧にあるものは「他の検索エンジン」には表示しないようにした 282 | - 0.1.2006121501 283 | * 公開 284 | -------------------------------------------------------------------------------- /licenses/MPL2.0.txt: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /webextensions/.gitignore: -------------------------------------------------------------------------------- 1 | *.xpi 2 | extlib/*.js 3 | 4 | # node.js dependency 5 | node_modules/ 6 | -------------------------------------------------------------------------------- /webextensions/Makefile: -------------------------------------------------------------------------------- 1 | NPM_MOD_DIR := $(CURDIR)/node_modules 2 | NPM_BIN_DIR := $(NPM_MOD_DIR)/.bin 3 | 4 | .PHONY: xpi install_dependency lint format init_extlib update_extlib install_extlib 5 | 6 | all: xpi 7 | 8 | install_dependency: 9 | [ -e "$(NPM_BIN_DIR)/eslint" -a -e "$(NPM_BIN_DIR)/jsonlint-cli" ] || npm install 10 | 11 | lint: install_dependency 12 | "$(NPM_BIN_DIR)/eslint" . --report-unused-disable-directives 13 | find . -type d -name node_modules -prune -o -type f -name '*.json' -print | xargs "$(NPM_BIN_DIR)/jsonlint-cli" 14 | 15 | format: install_dependency 16 | "$(NPM_BIN_DIR)/eslint" . --ext=.js --report-unused-disable-directives --fix 17 | 18 | xpi: init_extlib install_extlib lint 19 | rm -f ./*.xpi 20 | zip -r -9 secondsearch-we.xpi manifest.json common _locales background panel options resources extlib >/dev/null -x '*/.*' 2>/dev/null 21 | 22 | init_extlib: 23 | git submodule update --init 24 | 25 | update_extlib: 26 | git submodule foreach 'git checkout trunk || git checkout main || git checkout master && git pull' 27 | 28 | install_extlib: 29 | rm -rf extlib/*.js 30 | cp ../submodules/webextensions-lib-configs/Configs.js extlib/ 31 | cp ../submodules/webextensions-lib-l10n/l10n.js extlib/ 32 | cp ../submodules/webextensions-lib-options/Options.js extlib/ 33 | cp ../submodules/webextensions-lib-scroll/scroll.js extlib/ 34 | 35 | -------------------------------------------------------------------------------- /webextensions/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { "message": "Second Search" }, 3 | "extensionDescription": { "message": "Provides custom search field for smart keywords (bookmark keywords)." }, 4 | 5 | "panelTitle": { "message": "Search with Keyword" }, 6 | "commandDescription": { "message": "Search with Keyword" }, 7 | 8 | "searchField_placeholder": { "message": "Search with Keyword" }, 9 | "doSearch_label": { "message": "Search" }, 10 | 11 | "switchToRecentlyUsed_tooltip": { "message": "Switch to the view ordeded by Recently Used (Ctrl/⌘ - ←)" }, 12 | "switchToAll_tooltip": { "message": "Switch to the view ordeded by Name (Ctrl/⌘ - →)" }, 13 | 14 | "howToAdd_description": { "message": "There is no bookmark item for keyword search. To start search with keyword, plases right-click on a search field in any webpage and choose \"Add a keyword for this Search\", or just create a bookmark with a URL including \"%s\" as a paceholder for the search term. Then they will be listed here and you can search with keyword directly from this panel." }, 15 | 16 | "panel_searchPermission_label": { "message": "Integrate search engines of Firefox itself" }, 17 | 18 | 19 | "config_searchPermission_label": { "message": "Integrate search engines of Firefox itself" }, 20 | 21 | "config_theme_caption": { "message": "Color scheme" }, 22 | "config_theme_default": { "message": "Default" }, 23 | "config_theme_dark": { "message": "Dark" }, 24 | 25 | "config_defaultOpenIn_caption": { "message": "Open search result in" }, 26 | "config_defaultOpenIn_current": { "message": "the Current Tab" }, 27 | "config_defaultOpenIn_tab": { "message": "a New Tab" }, 28 | "config_defaultOpenIn_backgroundTab": { "message": "a New Background Tab (popup will stay open after search)" }, 29 | "config_defaultOpenIn_window": { "message": "aNew Window" }, 30 | "config_accelActionOpenIn_caption": { "message": "Open search result for actions with accel key (Ctrl/⌘) in" }, 31 | "config_recycleBlankCurrentTab_label": { "message": "Recycle current blank tab, instead of new tab" }, 32 | "config_recycleTabUrlPattern_label": { "message": "Recycle more tabs with URI matches to following regular expression:" }, 33 | "config_defaultEngine_label": { "message": "Default search engine when nothing item is selected (\"%s\" will be replaced to the search term):" }, 34 | "config_favIconProvider_label": { "message": "Icon provider (\"%s\" will be replaced to the origin part of the bookmark's URI:" }, 35 | "config_fillFieldWithSelectionText_label": { "message": "Fill search term field with the selection text in the current tab" }, 36 | "config_clearFieldAfterSearch_label": { "message": "Clear last search term for the next time" }, 37 | "config_clearFieldAfterSearchDelay_before": { "message": "when the next search is started after " }, 38 | "config_clearFieldAfterSearchDelay_after": { "message": "msec." }, 39 | "config_clearFocusByInput_label": { "message": "Clear focus on search engine by starting of input" }, 40 | "config_closeAfterSearch_label": { "message": "Close popup after search" }, 41 | "config_newTabDelay_before": { "message": "Delay to start search in a new tab:" }, 42 | "config_newTabDelay_after": { "message": "msec. (*Please increase this if the search result tab becomes blank unexpectedly for native search engines.)" }, 43 | "config_newWindowDelay_before": { "message": "Delay to start search in a new window:" }, 44 | "config_newWindowDelay_after": { "message": "msec. (*Please increase this if the search result window becomes blank unexpectedly for native search engines.)" }, 45 | 46 | "config_bookmarks_caption": { "message": "Search Engines from Bookmark Keywords" }, 47 | "config_bookmarks_clearCache_description": { "message": "This addon lists bookmarks with their own keyword as virtual search engines, but sometimes they are missing due to Firefox Sync and other triggers. You'll see flush old information and refreshed the list of such bookmarks, by clicking following button." }, 48 | "config_bookmarks_clearCache_label": { "message": "Refresh Search Engines from Bookmarks" }, 49 | 50 | "config_shortcuts_caption": { "message": "Keyboard shortcuts" }, 51 | 52 | "config_allUrlsPermission_label": { "message": "Grant cross origin access to the host of non-default icon provider (Turn this on if you cannot get icons for search engines)" }, 53 | 54 | "config_debug_caption": { "message": "Development" }, 55 | "config_debug_label": { "message": "Debug mode" }, 56 | "config_all_caption": { "message": "All Configs" } 57 | } 58 | -------------------------------------------------------------------------------- /webextensions/_locales/ja/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { "message": "Second Search - セカンドサーチ" }, 3 | "extensionDescription": { "message": "スマートキーワード(ブックマークキーワード)用の検索ボックスを提供します。" }, 4 | 5 | "panelTitle": { "message": "キーワード検索" }, 6 | "commandDescription": { "message": "キーワード検索" }, 7 | 8 | "searchField_placeholder": { "message": "キーワード検索" }, 9 | "doSearch_label": { "message": "検索" }, 10 | 11 | "switchToRecentlyUsed_tooltip": { "message": "一覧を最近使った順に切り替える(Ctrl/⌘-←)" }, 12 | "switchToAll_tooltip": { "message": "一覧を名前順に切り替える(Ctrl/⌘-→)" }, 13 | 14 | "howToAdd_description": { "message": "キーワード検索用のブックマーク項目がありません。Webページ上の検索ボックスで右クリックして「この検索にキーワードを設定」を選択してブックマークを作成するか、検索語句のプレースホルダとなる「%s」をURLに含めてブックマークを作成すると、この画面から直接検索できるようになります。" }, 15 | 16 | "panel_searchPermission_label": { "message": "Firefox自体の検索エンジンも利用する" }, 17 | 18 | 19 | "config_searchPermission_label": { "message": "Firefox自体の検索エンジンも利用する" }, 20 | 21 | "config_theme_caption": { "message": "配色" }, 22 | "config_theme_default": { "message": "既定" }, 23 | "config_theme_dark": { "message": "Dark" }, 24 | 25 | "config_defaultOpenIn_caption": { "message": "検索結果の既定の開き方" }, 26 | "config_defaultOpenIn_current": { "message": "現在のタブに読み込む" }, 27 | "config_defaultOpenIn_tab": { "message": "新しいタブを開く" }, 28 | "config_defaultOpenIn_backgroundTab": { "message": "新しいバックグラウンドのタブを開く(ポップアップは閉じられません)" }, 29 | "config_defaultOpenIn_window": { "message": "新しいウィンドウを開く" }, 30 | "config_accelActionOpenIn_caption": { "message": "アクセルキー(Ctrl/⌘)を押しながら検索した場合の検索結果の開き方" }, 31 | "config_recycleBlankCurrentTab_label": { "message": "現在のタブが空の時は、新しいタブを開かず現在のタブに読み込む" }, 32 | "config_recycleTabUrlPattern_label": { "message": "URIが以下の正規表現に当てはまるタブも空のタブと見なす:" }, 33 | "config_defaultEngine_label": { "message": "何も項目が選択されていない時に使う既定の検索エンジン(URI中の「%s」が検索語句に置き換わります):" }, 34 | "config_favIconProvider_label": { "message": "アイコンの取得先(URI中の「%s」がブックマークのURIのオリジン部分に置き換わります):" }, 35 | "config_fillFieldWithSelectionText_label": { "message": "現在のタブで選択されている文字列を検索語句として自動入力する" }, 36 | "config_clearFieldAfterSearch_label": { "message": "検索の実行後に検索語句を空にする" }, 37 | "config_clearFieldAfterSearchDelay_before": { "message": "ただし、" }, 38 | "config_clearFieldAfterSearchDelay_after": { "message": "ミリ秒以内にもう一度検索しようとした場合を除く" }, 39 | "config_clearFocusByInput_label": { "message": "入力を開始したら検索エンジンのフォーカス状態をリセットする" }, 40 | "config_closeAfterSearch_label": { "message": "検索を実行したらポップアップを閉じる" }, 41 | "config_newTabDelay_before": { "message": "新しいタブで検索を実行するまでの遅延:" }, 42 | "config_newTabDelay_after": { "message": "ミリ秒(ネイティブの検索エンジンでの検索結果のタブが意図せず空になってしまう場合、数値を増やして下さい)" }, 43 | "config_newWindowDelay_before": { "message": "新しいウィンドウで検索を実行するまでの遅延:" }, 44 | "config_newWindowDelay_after": { "message": "ミリ秒(ネイティブの検索エンジンでの検索結果のウィンドウが意図せず空になってしまう場合、数値を増やして下さい)" }, 45 | 46 | "config_bookmarks_caption": { "message": "ブックマークキーワードからの検索エンジン" }, 47 | "config_bookmarks_clearCache_description": { "message": "このアドオンはキーワード付きのブックマークを仮想敵な検索エンジンとして一覧に表示しますが、Firefox Syncやその他の理由により、いくつかのブックマークが認識されなくなる場合があります。その場合、以下のボタンをクリックする事でキャッシュを消去し、強制的にそのようなブックマークの情報を再スキャンさせることができます。" }, 48 | "config_bookmarks_clearCache_label": { "message": "ブックマークからの検索エンジンの情報を更新する" }, 49 | 50 | "config_shortcuts_caption": { "message": "キーボードショートカット" }, 51 | 52 | "config_allUrlsPermission_label": { "message": "初期設定のアイコン取得先以外へのクロスオリジンアクセスを許可する(アイコンを取得できない場合に有効にして下さい)" }, 53 | 54 | "config_debug_caption": { "message": "開発用" }, 55 | "config_debug_label": { "message": "デバッグモード" }, 56 | "config_all_caption": { "message": "すべての設定" } 57 | } 58 | -------------------------------------------------------------------------------- /webextensions/background/background.js: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 'use strict'; 7 | 8 | gLogContext = 'BG'; 9 | 10 | const SearchEngines = { 11 | cachedEngines: null, 12 | cachedEnginesById: null, 13 | recentlyUsedEngines: [], 14 | 15 | FAVICON_SIZE: 16, 16 | VALID_FAVICON_PATTERN: /^(about|app|chrome|data|file|ftp|https?|moz-extension|resource):/, 17 | DRAWABLE_FAVICON_PATTERN: /^(https?|moz-extension|resource):/, 18 | 19 | async reset() { 20 | log('reset'); 21 | const engines = []; 22 | const enginesById = {}; 23 | this.cachedEngines = []; 24 | this.cachedEnginesById = {}; 25 | await Promise.all([ 26 | this.collectBookmarksEngines(engines, enginesById), 27 | this.collectNativeEngines(engines, enginesById) 28 | ]); 29 | configs.cachedEnginesById = this.cachedEnginesById = enginesById; 30 | this.cachedEngines = engines; 31 | log('engines ', engines); 32 | this.sort(); 33 | }, 34 | async collectBookmarksEngines(engines, enginesById) { 35 | const bookmarks = await browser.bookmarks.search({ query: '%s' }); 36 | const promisedCompletes = []; 37 | for (const bookmark of bookmarks) { 38 | if (bookmark.type != 'bookmark' || 39 | !/%s/i.test(bookmark.url)) 40 | continue; 41 | if (!bookmark.favIconUrl) { 42 | promisedCompletes.push((async () => { 43 | try { 44 | const url = await this.getFavIconDataURI(bookmark); 45 | if (url) 46 | bookmark.favIconUrl = url; 47 | } 48 | catch(_e) { 49 | } 50 | })()); 51 | } 52 | bookmark._recentlyUsedIndex = configs.recentlyUsedEngines.indexOf(bookmark.id); 53 | engines.push(bookmark); 54 | enginesById[bookmark.id] = bookmark; 55 | } 56 | await Promise.all(promisedCompletes); 57 | }, 58 | async collectNativeEngines(engines, enginesById) { 59 | const available = await Permissions.isGranted(Permissions.SEARCH_PERMISSION); 60 | if (!available) 61 | return; 62 | const searchEngines = await browser.search.get(); 63 | for (const engine of searchEngines) { 64 | engine.id = 65 | engine.url = `search-engine:${engine.name}`; 66 | engine.title = engine.name; 67 | engine._recentlyUsedIndex = configs.recentlyUsedEngines.indexOf(engine.id); 68 | engines.push(engine); 69 | enginesById[engine.id] = engine; 70 | } 71 | }, 72 | 73 | async updateNativeEngines() { 74 | const engines = []; 75 | const enginesById = []; 76 | await this.collectNativeEngines(engines, enginesById); 77 | 78 | let updated = false; 79 | 80 | // handle removed engines 81 | for (const id of Object.keys(this.cachedEnginesById)) { 82 | if (!/search-engine:/.test(id) || 83 | id in enginesById) 84 | continue; 85 | delete this.cachedEnginesById[id]; 86 | updated = true; 87 | } 88 | 89 | // handle added engines 90 | for (const engine of engines) { 91 | if (engine.id in this.cachedEnginesById) 92 | continue; 93 | this.cachedEnginesById[engine.id] = engine; 94 | updated = true; 95 | } 96 | 97 | if (updated) { 98 | configs.cachedEnginesById = this.cachedEnginesById 99 | this.updateCache(); 100 | } 101 | }, 102 | 103 | buildFavIconURI(aEngine) { 104 | const uriMatch = aEngine.url.match(/^(\w+:\/\/[^\/]+)/); 105 | if (!uriMatch) 106 | return null; 107 | return configs.favIconProvider.replace(/%s/gi, uriMatch[1]); 108 | }, 109 | 110 | async getFavIconDataURI(aEngine) { 111 | const url = this.buildFavIconURI(aEngine); 112 | if (url && url.startsWith('data:')) 113 | return url; 114 | 115 | if (!url || 116 | !this.VALID_FAVICON_PATTERN.test(url) || 117 | !this.DRAWABLE_FAVICON_PATTERN.test(url)) 118 | return null; 119 | 120 | log('getFavIconDataURI: fetch ', url); 121 | 122 | return new Promise((resolve, reject) => { 123 | const image = new Image(); 124 | if (/^https?:/.test(url)) 125 | image.crossOrigin = 'anonymous'; 126 | image.addEventListener('load', () => { 127 | this.setupCanvas(); 128 | const context = this.canvas.getContext('2d'); 129 | context.clearRect(0, 0, this.FAVICON_SIZE, this.FAVICON_SIZE); 130 | context.drawImage(image, 0, 0, this.FAVICON_SIZE, this.FAVICON_SIZE); 131 | try { 132 | const dataURI = this.canvas.toDataURL('image/png'); 133 | log('getFavIconDataURI: data = ', dataURI); 134 | resolve(dataURI); 135 | } 136 | catch(error) { 137 | // it can fail due to security reasons 138 | log('getFavIconDataURI (after load): ', url, error); 139 | resolve(url); 140 | } 141 | }); 142 | image.addEventListener('error', error => { 143 | log('getFavIconDataURI: ', url, error); 144 | reject(error); 145 | }); 146 | image.src = url; 147 | }); 148 | }, 149 | setupCanvas() { 150 | if (this.canvas) 151 | return; 152 | this.canvas = document.createElement('canvas'); 153 | this.canvas.width = this.canvas.height = this.FAVICON_SIZE; 154 | this.canvas.setAttribute('style', ` 155 | visibility: hidden; 156 | pointer-events: none; 157 | position: fixed 158 | `); 159 | document.body.appendChild(this.canvas); 160 | }, 161 | 162 | updateCache() { 163 | this.cachedEngines = []; 164 | for (const id of Object.keys(this.cachedEnginesById)) { 165 | this.cachedEngines.push(this.cachedEnginesById[id]); 166 | } 167 | SearchEngines.sort(); 168 | }, 169 | 170 | sort() { 171 | log('sort'); 172 | for (const id of Object.keys(this.cachedEnginesById)) { 173 | const engine = this.cachedEnginesById[id]; 174 | engine._recentlyUsedIndex = this.recentlyUsedEngines.indexOf(id); 175 | } 176 | log('recentlyUsedEngines sorted'); 177 | this.cachedEngines.sort((aA, aB) => 178 | aA._recentlyUsedIndex < 0 && aB._recentlyUsedIndex > -1 ? 179 | 1 : 180 | aA._recentlyUsedIndex > -1 && aB._recentlyUsedIndex < 0 ? 181 | -1 : 182 | aA._recentlyUsedIndex - aB._recentlyUsedIndex || 183 | aA.title > aB.title); 184 | log('cachedEngines sorted'); 185 | }, 186 | 187 | onUsed(aRecentlyUsedId) { 188 | this.recentlyUsedEngines = this.recentlyUsedEngines.filter(aId => aId != aRecentlyUsedId); 189 | this.recentlyUsedEngines.unshift(aRecentlyUsedId); 190 | log('updated recently used engines: ', this.recentlyUsedEngines); 191 | configs.recentlyUsedEngines = this.recentlyUsedEngines; 192 | this.sort(); 193 | }, 194 | 195 | async onBookmarkCreated(aId, aMayBeEngine) { 196 | if (aMayBeEngine.type != 'bookmark' || 197 | !/%s/i.test(aMayBeEngine.url)) 198 | return; 199 | log('new engine is added: ', aMayBeEngine); 200 | this.cachedEnginesById[aId] = aMayBeEngine; 201 | if (!aMayBeEngine.favIconUrl) { 202 | try { 203 | aMayBeEngine.favIconUrl = await this.getFavIconDataURI(aMayBeEngine); 204 | } 205 | catch(_e) { 206 | } 207 | } 208 | configs.cachedEnginesById = this.cachedEnginesById; 209 | this.updateCache(); 210 | }, 211 | 212 | onBookmarkRemoved(aId, aRemoveInfo) { 213 | if (aId in this.cachedEnginesById) { 214 | log('engine is removed: ', this.cachedEnginesById[aId]); 215 | delete this.cachedEnginesById[aId]; 216 | configs.cachedEnginesById = this.cachedEnginesById; 217 | this.updateCache(); 218 | } 219 | }, 220 | 221 | onBookmarkChanged(aId, aChangeInfo) { 222 | if ('url' in aChangeInfo) { 223 | if (aId in this.cachedEnginesById && 224 | !/%s/i.test(aChangeInfo.url)) { 225 | log('engine is removed by changing URL: ', this.cachedEnginesById[aId]); 226 | delete this.cachedEnginesById[aId]; 227 | configs.cachedEnginesById = this.cachedEnginesById; 228 | this.updateCache(); 229 | } 230 | else if (/%s/i.test(aChangeInfo.url)) { 231 | (async () => { 232 | let bookmark = await browser.bookmarks.get(aId); 233 | if (Array.isArray(bookmark)) 234 | bookmark = bookmark[0]; 235 | log('engine is added by changing URL: ', bookmark); 236 | this.cachedEnginesById[aId] = bookmark; 237 | if (!bookmark.favIconUrl) { 238 | try { 239 | bookmark.favIconUrl = await this.getFavIconDataURI(bookmark); 240 | } 241 | catch(_e) { 242 | } 243 | } 244 | configs.cachedEnginesById = this.cachedEnginesById; 245 | this.updateCache(); 246 | })(); 247 | } 248 | } 249 | if ('title' in aChangeInfo && 250 | aId in this.cachedEnginesById) { 251 | this.cachedEnginesById[aId].title = aChangeInfo.title; 252 | configs.cachedEnginesById = this.cachedEnginesById; 253 | this.updateCache(); 254 | } 255 | }, 256 | 257 | async cleanupMissingEngines() { 258 | log('cleanupMissingEngines'); 259 | const ids = Object.keys(this.cachedEnginesById).sort(); 260 | log('ids: ', ids); 261 | let bookmarks = await Promise.all(ids.map(aId => browser.bookmarks.get(aId).catch(aError => null))); 262 | if (bookmarks.length > 0) { 263 | if (Array.isArray(bookmarks[0])) 264 | bookmarks = Array.prototype.concat.apply([], bookmarks); 265 | bookmarks = bookmarks.filter(aBookmark => !!aBookmark).map(aBookmark => aBookmark.id).sort(); 266 | } 267 | log('actual bookmarks: ', bookmarks); 268 | if (ids.join('\n') != bookmarks.join('\n')) { 269 | for (const id of ids) { 270 | if (bookmarks.indexOf(id) > -1) 271 | continue; 272 | log('delete ', id); 273 | delete this.cachedEnginesById[id]; 274 | } 275 | configs.cachedEnginesById = this.cachedEnginesById; 276 | } 277 | }, 278 | 279 | async doSearch(params) { 280 | const engine = params.engineId ? this.cachedEnginesById[params.engineId] : null; 281 | log('do search by engine: ', engine); 282 | 283 | const useDefaultNativeEngine = !engine && await Permissions.isGranted(Permissions.SEARCH_PERMISSION); 284 | const isNativeEngine = engine && /^search-engine:/.test(params.engineId); 285 | log('useDefaultNativeEngine: ', useDefaultNativeEngine); 286 | log('isNativeEngine: ', isNativeEngine); 287 | 288 | if (useDefaultNativeEngine || isNativeEngine) 289 | await this.doSearchByNativeEngine(engine, params); 290 | else 291 | await this.doSearchByBookmarkEngine(engine, params); 292 | 293 | if (params.save && params.engineId) 294 | this.onUsed(params.engineId); 295 | }, 296 | async doSearchByNativeEngine(engine, params) { 297 | const searchParams = { 298 | query: params.term 299 | }; 300 | if (engine) 301 | searchParams.engine = engine.name; 302 | 303 | log('doSearchByNativeEngine ', engine, params); 304 | const url = 'about:blank'; 305 | switch (params.where) { 306 | case kOPEN_IN_TAB: 307 | case kOPEN_IN_BACKGROUND_TAB: { 308 | let tabParams = { 309 | active: params.where != kOPEN_IN_BACKGROUND_TAB, 310 | url 311 | }; 312 | if (params.openerTabId) 313 | tabParams.openerTabId = params.openerTabId; 314 | const tab = await browser.tabs.create(tabParams); 315 | searchParams.tabId = tab.id; 316 | this.nativeSearchTabLastStatus.set(tab.id, null); 317 | if (configs.newTabDelay > 0) 318 | await wait(configs.newTabDelay); 319 | }; break; 320 | 321 | case kOPEN_IN_WINDOW: { 322 | const window = await browser.windows.create({ url }); 323 | const tab = window.tabs[0]; 324 | searchParams.tabId = tab.id; 325 | this.nativeSearchTabLastStatus.set(tab.id, null); 326 | if (configs.newWindowDelay > 0) 327 | await wait(configs.newWindowDelay); 328 | }; break; 329 | 330 | default: 331 | searchParams.tabId = params.tabId; 332 | break; 333 | } 334 | 335 | if (this.nativeSearchTabLastStatus.has(searchParams.tabId)) { 336 | // The new tab will be loaded with "about:bank" twice until it becomes available to be a new search tab, 337 | // so we cannot do search simply after only one "completely loaded" event. 338 | await new Promise(async (resolve, _reject) => { 339 | const startAt = Date.now(); 340 | while (this.nativeSearchTabLastStatus.get(searchParams.tabId) != 'complete' && 341 | Date.now() - startAt <= configs.searchTimeout) { 342 | await wait(100); 343 | } 344 | this.nativeSearchTabLastStatus.delete(searchParams.tabId); 345 | resolve(); 346 | }); 347 | } 348 | 349 | log('browser.search.search() called with params: ', searchParams); 350 | await browser.search.search(searchParams); 351 | }, 352 | async doSearchByBookmarkEngine(engine, params) { 353 | let url = engine ? engine.url : configs.defaultEngine; 354 | url = url.replace(/%s/gi, encodeURIComponent(params.term) || ''); 355 | 356 | log('doSearchByBookmarkEngine ', engine, params); 357 | switch (params.where) { 358 | case kOPEN_IN_TAB: 359 | case kOPEN_IN_BACKGROUND_TAB: { 360 | let tabParams = { 361 | active: params.where != kOPEN_IN_BACKGROUND_TAB, 362 | url 363 | }; 364 | if (params.openerTabId) 365 | tabParams.openerTabId = params.openerTabId; 366 | await browser.tabs.create(tabParams); 367 | }; break; 368 | 369 | case kOPEN_IN_WINDOW: 370 | await browser.windows.create({ url }); 371 | break; 372 | 373 | default: 374 | await browser.tabs.update(params.tabId, { url }); 375 | break; 376 | } 377 | }, 378 | 379 | nativeSearchTabLastStatus: new Map(), 380 | onTabUpdated(tabId, updateInfo, tab) { 381 | if (!this.nativeSearchTabLastStatus.has(tabId)) 382 | return; 383 | this.nativeSearchTabLastStatus.set(tabId, updateInfo.status); 384 | }, 385 | }; 386 | 387 | configs.$loaded.then(async () => { 388 | let isInitialInstall = false; 389 | configs.lastSearchTerm = ''; 390 | browser.bookmarks.onCreated.addListener(SearchEngines.onBookmarkCreated.bind(SearchEngines)); 391 | browser.bookmarks.onRemoved.addListener(SearchEngines.onBookmarkRemoved.bind(SearchEngines)); 392 | browser.bookmarks.onChanged.addListener(SearchEngines.onBookmarkChanged.bind(SearchEngines)); 393 | browser.tabs.onUpdated.addListener(SearchEngines.onTabUpdated.bind(SearchEngines), { 394 | properties: ['status'], 395 | }); 396 | if (!configs.cachedEnginesById) { 397 | log('initial install'); 398 | SearchEngines.reset(); 399 | isInitialInstall = true; 400 | } 401 | else { 402 | log('restore engines'); 403 | SearchEngines.cachedEnginesById = configs.cachedEnginesById || {}; 404 | await SearchEngines.cleanupMissingEngines(); 405 | SearchEngines.recentlyUsedEngines = configs.recentlyUsedEngines || []; 406 | SearchEngines.updateCache(); 407 | } 408 | 409 | const kCONFIGS_VERSION = 2; 410 | switch (configs.configsVersion) { 411 | case 0: 412 | case 1: 413 | if (!isInitialInstall) 414 | SearchEngines.reset(); // clear cache to fetch favicons 415 | } 416 | configs.configsVersion = kCONFIGS_VERSION; 417 | 418 | configs.$addObserver(key => { 419 | switch (key) { 420 | case 'cachedEnginesById': 421 | if (!configs.cachedEnginesById) 422 | SearchEngines.reset(); 423 | break; 424 | 425 | default: 426 | break; 427 | } 428 | }); 429 | }); 430 | 431 | browser.runtime.onMessage.addListener((message, sender) => { 432 | if (!message || 433 | typeof message.type != 'string' || 434 | message.type.indexOf('secondsearch:') != 0) 435 | return; 436 | 437 | switch (message.type) { 438 | case kCOMMAND_GET_SEARCH_ENGINES: 439 | log('get engines , SearchEngines.cachedEngines'); 440 | return SearchEngines.updateNativeEngines() 441 | .then(() => SearchEngines.cachedEngines); 442 | 443 | case kCOMMAND_DO_SEARCH: 444 | log('do search ', message); 445 | return new Promise((resolve, reject) => { 446 | setTimeout(() => { 447 | SearchEngines.doSearch(message).then(resolve); 448 | }, 100); 449 | }); 450 | } 451 | }); 452 | 453 | 454 | const mDarkModeMatchMedia = window.matchMedia('(prefers-color-scheme: dark)'); 455 | 456 | const ORIGINAL_ICONS = { 457 | 16: '/resources/16x16.svg', 458 | 32: '/resources/32x32.svg', 459 | }; 460 | const ICONS = JSON.parse(JSON.stringify(ORIGINAL_ICONS)); 461 | 462 | async function updateIconForBrowserTheme(theme) { 463 | // generate icons with theme specific color 464 | if (!theme) { 465 | const window = await browser.windows.getLastFocused(); 466 | theme = await browser.theme.getCurrent(window.id); 467 | } 468 | 469 | log('updateIconForBrowserTheme: ', theme); 470 | if (theme.colors) { 471 | const actionIconColor = theme.colors.icons || theme.colors.toolbar_text || theme.colors.tab_text || theme.colors.tab_background_text || theme.colors.bookmark_text || theme.colors.textcolor; 472 | await Promise.all(Array.from(Object.entries(ORIGINAL_ICONS), async ([state, url]) => { 473 | const response = await fetch(url); 474 | const body = await response.text(); 475 | const actionIconSource = body.replace(/transparent\s*\/\*\s*TO BE REPLACED WITH THEME COLOR\s*\*\//g, actionIconColor); 476 | ICONS[state] = `data:image/svg+xml,${escape(actionIconSource)}#toolbar-theme`; 477 | })); 478 | } 479 | else { 480 | for (const [state, url] of Object.entries(ORIGINAL_ICONS)) { 481 | ICONS[state] = `${url}#toolbar`; 482 | } 483 | } 484 | 485 | log('updateIconForBrowserTheme: applying icons: ', ICONS); 486 | 487 | await browser.browserAction.setIcon({ 488 | path: ICONS, 489 | }); 490 | } 491 | 492 | browser.theme.onUpdated.addListener(updateInfo => { 493 | updateIconForBrowserTheme(updateInfo.theme); 494 | }); 495 | 496 | mDarkModeMatchMedia.addListener(async _event => { 497 | updateIconForBrowserTheme(); 498 | }); 499 | 500 | /* 501 | configs.$addObserver(key => { 502 | switch (key) { 503 | default; 504 | break; 505 | } 506 | }); 507 | */ 508 | configs.$loaded.then(() => updateIconForBrowserTheme()); 509 | -------------------------------------------------------------------------------- /webextensions/common/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 'use strict'; 7 | 8 | var configs; 9 | var gLogContext = '?'; 10 | 11 | function log(aMessage, ...aArgs) 12 | { 13 | if (!configs || !configs.debug) 14 | return; 15 | 16 | var nest = (new Error()).stack.split('\n').length; 17 | var indent = ''; 18 | for (let i = 0; i < nest; i++) { 19 | indent += ' '; 20 | } 21 | console.log(`ss<${gLogContext}>: ${indent}${aMessage}`, ...aArgs); 22 | } 23 | 24 | async function wait(aTask = 0, aTimeout = 0) { 25 | if (typeof aTask != 'function') { 26 | aTimeout = aTask; 27 | aTask = null; 28 | } 29 | return new Promise((aResolve, aReject) => { 30 | setTimeout(async () => { 31 | if (aTask) 32 | await aTask(); 33 | aResolve(); 34 | }, aTimeout); 35 | }); 36 | } 37 | 38 | function nextFrame() { 39 | return new Promise((aResolve, aReject) => { 40 | window.requestAnimationFrame(aResolve); 41 | }); 42 | } 43 | 44 | configs = new Configs({ 45 | cachedEnginesById: null, 46 | recentlyUsedEngines: [], 47 | autocomplete: false, 48 | history: [], 49 | maxHistoryCount: 100, 50 | theme: 'default', 51 | iconColor: 'auto', 52 | fillFieldWithSelectionText: true, 53 | clearFieldAfterSearch: true, 54 | clearFieldAfterSearchDelay: 5000, 55 | clearFocusByInput: true, 56 | lastSearchTerm: '', 57 | lastSearchTime: 0, 58 | closeAfterSearch: true, 59 | recycleBlankCurrentTab: true, 60 | recycleTabUrlPattern: '^about:(newtab|home|privatebrowsing)$', 61 | defaultOpenIn: kOPEN_IN_TAB, 62 | accelActionOpenIn: kOPEN_IN_BACKGROUND_TAB, 63 | defaultEngine: 'https://www.google.com/search?q=%s', 64 | favIconProvider: `https://www.google.com/s2/favicons?domain=%s`, 65 | focusDelay: 150, 66 | smoothScrollDuration: 150, 67 | newWindowDelay: 1000, 68 | newTabDelay: 100, 69 | searchTimeout: 2000, 70 | applyThemeColorToIcon: false, 71 | configsVersion: 0, 72 | debug: false 73 | }, { 74 | localKeys: ` 75 | cachedEnginesById 76 | recentlyUsedEngines 77 | theme 78 | lastSearchTerm 79 | lastSearchTime 80 | debug 81 | `.trim().split('\n').map(aKey => aKey.trim()).filter(aKey => aKey && aKey.indexOf('//') != 0) 82 | }); 83 | 84 | const RTL_LANGUAGES = new Set([ 85 | 'ar', 86 | 'he', 87 | 'fa', 88 | 'ur', 89 | 'ps', 90 | 'sd', 91 | 'ckb', 92 | 'prs', 93 | 'rhg', 94 | ]); 95 | 96 | function isRTL() { 97 | const lang = ( 98 | navigator.language || 99 | navigator.userLanguage || 100 | //(new Intl.DateTimeFormat()).resolvedOptions().locale || 101 | '' 102 | ).split('-')[0]; 103 | return RTL_LANGUAGES.has(lang); 104 | } 105 | -------------------------------------------------------------------------------- /webextensions/common/constants.js: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 'use strict'; 7 | 8 | const kCOMMAND_GET_SEARCH_ENGINES = 'secondsearch:get-search-engines'; 9 | const kCOMMAND_DO_SEARCH = 'secondsearch:do-search'; 10 | 11 | const kOPEN_IN_CURRENT = 0; 12 | const kOPEN_IN_TAB = 1; 13 | const kOPEN_IN_WINDOW = 2; 14 | const kOPEN_IN_BACKGROUND_TAB = 3; 15 | 16 | const kOPERATED_BY_KEY = 0; 17 | const kOPERATED_BY_MOUSE = 1; 18 | 19 | -------------------------------------------------------------------------------- /webextensions/common/permissions.js: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 'use strict'; 7 | 8 | const Permissions = { 9 | SEARCH_PERMISSION: { permissions: ['search'] }, 10 | ALL_URLS: { origins: [''] }, 11 | 12 | async isGranted(permission) { 13 | try { 14 | return browser.permissions.contains(permission); 15 | } 16 | catch(e) { 17 | return false; 18 | } 19 | }, 20 | 21 | async initUI(params = {}) { 22 | const checkbox = params.checkbox; 23 | const permission = params.permission; 24 | const onChange = params.onChange; 25 | try { 26 | checkbox.checked = await browser.permissions.contains(permission); 27 | let lastState = checkbox.checked; 28 | checkbox.addEventListener('change', async () => { 29 | if (!checkbox.checked) { 30 | await browser.permissions.remove(permission); 31 | } 32 | else { 33 | checkbox.checked = await browser.permissions.request(permission); 34 | } 35 | if (lastState == checkbox.checked) 36 | return; 37 | if (typeof onChange == 'function') 38 | onChange(); 39 | lastState = checkbox.checked; 40 | }); 41 | } 42 | catch(e) { 43 | checkbox.setAttribute('disabled', true); 44 | checkbox.parentNode.setAttribute('disabled', true); 45 | } 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /webextensions/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | 3 | export default [{ 4 | ignores: ["eslint.config.mjs", "extlib/*", "!**/.eslintrc.js"], 5 | }, { 6 | languageOptions: { 7 | globals: { 8 | ...globals.browser, 9 | ...globals.webextensions, 10 | }, 11 | 12 | ecmaVersion: 2018, 13 | sourceType: "module", 14 | }, 15 | 16 | settings: { 17 | "import/resolver": { 18 | "babel-module": { 19 | root: ["./"], 20 | }, 21 | }, 22 | }, 23 | 24 | rules: { 25 | indent: ["warn", 2, { 26 | SwitchCase: 1, 27 | MemberExpression: 1, 28 | 29 | CallExpression: { 30 | arguments: "first", 31 | }, 32 | 33 | VariableDeclarator: { 34 | var: 2, 35 | let: 2, 36 | const: 3, 37 | }, 38 | }], 39 | 40 | quotes: ["warn", "single", { 41 | avoidEscape: true, 42 | allowTemplateLiterals: true, 43 | }], 44 | }, 45 | }]; -------------------------------------------------------------------------------- /webextensions/extlib/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piroor/secondsearch/ffb2349ffaac5a6c6f8b2571866f8048959b7579/webextensions/extlib/.gitkeep -------------------------------------------------------------------------------- /webextensions/icon-source/16x16-dark.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piroor/secondsearch/ffb2349ffaac5a6c6f8b2571866f8048959b7579/webextensions/icon-source/16x16-dark.ai -------------------------------------------------------------------------------- /webextensions/icon-source/16x16-light.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piroor/secondsearch/ffb2349ffaac5a6c6f8b2571866f8048959b7579/webextensions/icon-source/16x16-light.ai -------------------------------------------------------------------------------- /webextensions/icon-source/32x32-dark.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piroor/secondsearch/ffb2349ffaac5a6c6f8b2571866f8048959b7579/webextensions/icon-source/32x32-dark.ai -------------------------------------------------------------------------------- /webextensions/icon-source/32x32-light.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piroor/secondsearch/ffb2349ffaac5a6c6f8b2571866f8048959b7579/webextensions/icon-source/32x32-light.ai -------------------------------------------------------------------------------- /webextensions/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "__MSG_extensionName__", 4 | "version": "2.3.9", 5 | "author": "YUKI \"Piro\" Hiroshi", 6 | "description": "__MSG_extensionDescription__", 7 | "permissions": [ 8 | "https://www.google.com/s2/favicons?*", 9 | "https://*.gstatic.com/favicon*", 10 | "activeTab", 11 | "bookmarks", 12 | "search", 13 | "storage", 14 | "theme" 15 | ], 16 | "optional_permissions": [ 17 | "" 18 | ], 19 | "icons": { 20 | "16": "/resources/16x16.svg#default", 21 | "20": "/resources/16x16.svg#default", 22 | "24": "/resources/32x32.svg#default", 23 | "32": "/resources/32x32.svg#default" 24 | }, 25 | "background": { 26 | "scripts": [ 27 | "/common/constants.js", 28 | "/extlib/Configs.js", 29 | "/common/common.js", 30 | "/common/permissions.js", 31 | "/background/background.js" 32 | ] 33 | }, 34 | "browser_action": { 35 | "default_title": "__MSG_panelTitle__", 36 | "default_icon": { 37 | "16": "/resources/16x16.svg#toolbar", 38 | "32": "/resources/32x32.svg#toolbar" 39 | }, 40 | "browser_style": false, 41 | "default_popup": "panel/panel.html" 42 | }, 43 | "commands": { 44 | "_execute_browser_action": { 45 | "description": "__MSG_commandDescription__", 46 | "suggested_key": { 47 | "default": "Ctrl+Shift+L" 48 | } 49 | } 50 | }, 51 | "options_ui": { 52 | "page": "/options/options.html", 53 | "browser_style": false 54 | }, 55 | "default_locale": "en", 56 | "applications": { 57 | "gecko": { 58 | "id": "{0AE5CAA4-8BAB-11DB-AF59-ED4B56D89593}", 59 | "strict_min_version": "67.0" 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /webextensions/options/init.js: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 'use strict'; 7 | 8 | gLogContext = 'Options'; 9 | var options = new Options(configs); 10 | 11 | function onConfigChanged(aKey) { 12 | switch (aKey) { 13 | case 'debug': 14 | if (configs.debug) 15 | document.documentElement.classList.add('debugging'); 16 | else 17 | document.documentElement.classList.remove('debugging'); 18 | break; 19 | } 20 | } 21 | 22 | async function updateDefaultEngineUI() { 23 | const engineField = document.getElementById('defaultEngine'); 24 | const tabDelayField = document.getElementById('newTabDelay'); 25 | const windowDelayField = document.getElementById('newWindowDelay'); 26 | if (await Permissions.isGranted(Permissions.SEARCH_PERMISSION)) { 27 | engineField.setAttribute('disabled', true); 28 | engineField.parentNode.setAttribute('disabled', true); 29 | tabDelayField.removeAttribute('disabled'); 30 | tabDelayField.parentNode.removeAttribute('disabled'); 31 | windowDelayField.removeAttribute('disabled'); 32 | windowDelayField.parentNode.removeAttribute('disabled'); 33 | } 34 | else { 35 | engineField.removeAttribute('disabled'); 36 | engineField.parentNode.removeAttribute('disabled'); 37 | tabDelayField.setAttribute('disabled', true); 38 | tabDelayField.parentNode.setAttribute('disabled', true); 39 | windowDelayField.setAttribute('disabled', true); 40 | windowDelayField.parentNode.setAttribute('disabled', true); 41 | } 42 | } 43 | 44 | configs.$addObserver(onConfigChanged); 45 | window.addEventListener('DOMContentLoaded', async () => { 46 | document.documentElement.classList.toggle('rtl', isRTL()); 47 | await configs.$loaded; 48 | 49 | options.buildUIForAllConfigs(document.querySelector('#debug-configs')); 50 | onConfigChanged('debug'); 51 | 52 | /* 53 | const searchPermissionCheck = document.getElementById('searchPermission'); 54 | Permissions.initUI({ 55 | checkbox: searchPermissionCheck, 56 | permission: Permissions.SEARCH_PERMISSION, 57 | onChange() { 58 | configs.cachedEnginesById = null; 59 | updateDefaultEngineUI(); 60 | } 61 | }); 62 | */ 63 | updateDefaultEngineUI(); 64 | 65 | const clearCache = () => { 66 | configs.cachedEnginesById = null; 67 | }; 68 | const clearCacheButton = document.getElementById('clearCache'); 69 | clearCacheButton.addEventListener('click', event => { 70 | if (event.button == 0) 71 | clearCache(); 72 | }); 73 | clearCacheButton.addEventListener('keydown', event => { 74 | if (event.key =='Enter') 75 | clearCache(); 76 | }); 77 | 78 | const allUrlsPermissionCheck = document.getElementById('allUrlsPermission'); 79 | Permissions.initUI({ 80 | checkbox: allUrlsPermissionCheck, 81 | permission: Permissions.ALL_URLS, 82 | onChange() { 83 | if (allUrlsPermissionCheck.checked) 84 | clearCache(); 85 | } 86 | }); 87 | 88 | document.documentElement.classList.add('initialized'); 89 | }, { once: true }); 90 | 91 | -------------------------------------------------------------------------------- /webextensions/options/options.css: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | @import url(/resources/ui-base.css); 8 | @import url(/resources/ui-color.css); 9 | 10 | :root > * { 11 | transition: opacity 0.25s ease-out; 12 | } 13 | :root:not(.initialized) > * { 14 | opacity: 0; 15 | } 16 | 17 | :root.rtl { 18 | direction: rtl; 19 | } 20 | 21 | body { 22 | background: var(--bg-color); 23 | color: var(--text-color); 24 | } 25 | 26 | :root:not(.debugging) #debug-configs { 27 | max-height: 0; 28 | overflow: hidden; 29 | } 30 | 31 | :root:not(.debugging) #debug-configs * { 32 | -moz-user-focus: ignore; 33 | -moz-user-input: disabled; 34 | } 35 | 36 | 37 | p, ul { 38 | margin: 0 0 0.5em; 39 | padding: 0; 40 | } 41 | 42 | ul, 43 | ul li { 44 | list-style: none; 45 | } 46 | 47 | p.sub { 48 | margin-inline-start: 2em; 49 | } 50 | 51 | ul p.sub { 52 | margin-block: 0; 53 | } 54 | 55 | 56 | label[disabled="true"] { 57 | opacity: 0.5; 58 | } 59 | label[disabled="true"] input { 60 | opacity: 1; 61 | } 62 | 63 | .default-search-options label[disabled="true"], 64 | .default-search-options label[disabled="true"] input { 65 | display: none; 66 | -moz-user-focus: ignore; 67 | -moz-user-input: disabled; 68 | } 69 | -------------------------------------------------------------------------------- /webextensions/options/options.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 |

__MSG_config_theme_caption__ 25 | 28 | 31 |

32 | 33 |
34 | __MSG_config_defaultOpenIn_caption__ 35 |
    36 |
  • 39 |
  • 42 |

    43 |

    46 |

    47 |

  • 51 |
  • 54 |
55 |

58 |
59 |
60 | __MSG_config_accelActionOpenIn_caption__ 61 |
    62 |
  • 65 |
  • 68 |
  • 71 |
  • 74 |
75 |
76 | 77 |
78 |

82 |

87 |

92 |
93 |

96 |

99 |

100 |

105 |

108 | 109 |

__MSG_config_bookmarks_caption__

110 |

__MSG_config_bookmarks_clearCache_description__

111 |

112 | 113 |

__MSG_config_debug_caption__

114 |

118 |

121 |

124 |
125 |

__MSG_config_all_caption__

126 |
127 | 128 | 129 | -------------------------------------------------------------------------------- /webextensions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secondsearch-we", 3 | "version": "0.0.0", 4 | "engines": { 5 | "node": ">=8.6.0" 6 | }, 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "dependencies": { 11 | "babel-core": "^6.26.3", 12 | "babel-plugin-module-resolver": "^3.0.0", 13 | "eslint": "^9.15.0", 14 | "eslint-import-resolver-babel-module": "^4.0.0", 15 | "eslint-plugin-import": "^2.13.0", 16 | "jsonlint-cli": "*", 17 | "tunnel-agent": ">=0.6.0" 18 | }, 19 | "devDependencies": { 20 | "globals": "^15.12.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /webextensions/panel/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | /*eslint-env commonjs*/ 7 | /*eslint quote-props: ['error', "always"] */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = { 12 | 'extends': [ 13 | '../tools/eslint/for-module.js', 14 | ], 15 | }; 16 | -------------------------------------------------------------------------------- /webextensions/panel/panel.css: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | @import url(/resources/ui-base.css); 8 | @import url(/resources/ui-color.css); 9 | 10 | :root { 11 | /* https://dxr.mozilla.org/mozilla-central/rev/b056526be38e96b3e381b7e90cd8254ad1d96d9d/browser/themes/windows/browser.css#45 */ 12 | --focused-item-bgcolor: var(--in-content-button-background); 13 | --focused-item-textcolor: var(--in-content-text-color); 14 | --search-field-bordercolor: var(--in-content-box-border-color-mixed); 15 | --search-field-bgcolor: var(--in-content-box-background); 16 | --search-field-textcolor: var(--in-content-text-color); 17 | --bounding-margin: 0.85em; 18 | } 19 | 20 | :root[data-theme="dark"] { 21 | --bg-color: var(--dark-popup); 22 | --text-color: var(--dark-popup-text); 23 | --in-content-page-background: #2A2A2E /* rgb(42, 42, 46) */; 24 | --in-content-button-background: rgba(249,249,250,0.1); 25 | --in-content-border-hover: rgba(249,249,250,0.3); 26 | 27 | --in-content-box-background: var(--dark-popup); 28 | --in-content-box-border-color-mixed: var(--dark-popup-border); 29 | --in-content-text-color: var(--dark-popup-text); 30 | --search-field-bordercolor: var(--dark-toolbar-field-border); 31 | --search-field-bgcolor: var(--dark-toolbar-field); 32 | --search-field-textcolor: var(--dark-toolbar-field-text); 33 | /* 34 | --tab-active-marker: #0a84ff; 35 | --tab-active-marker-inactive: rgba(255, 255, 255, 0.2); 36 | */ 37 | scrollbar-color: var(--in-content-border-hover) var(--in-content-page-background); 38 | } 39 | 40 | @media (prefers-color-scheme: dark) { 41 | :root { 42 | --in-content-box-background: var(--dark-popup); 43 | --in-content-box-border-color-mixed: var(--dark-popup-border); 44 | --in-content-text-color: var(--dark-popup-text); 45 | --search-field-bordercolor: var(--dark-toolbar-field-border); 46 | --search-field-bgcolor: var(--dark-toolbar-field); 47 | --search-field-textcolor: var(--dark-toolbar-field-text); 48 | scrollbar-color: var(--in-content-border-hover) var(--in-content-page-background); 49 | } 50 | } 51 | 52 | :root.rtl { 53 | direction: rtl; 54 | } 55 | 56 | body { 57 | background: var(--bg-color); 58 | color: var(--text-color); 59 | display: flex; 60 | flex-direction: column; 61 | flex-wrap: nowrap; 62 | line-height: 1; 63 | } 64 | 65 | #description { 66 | display: none; 67 | line-height: 1.5; 68 | margin: 0.5em; 69 | } 70 | 71 | #description p { 72 | margin: 0 0 0.5em; 73 | } 74 | 75 | :root.no-engine #description { 76 | display: block; 77 | max-width: 30em; 78 | } 79 | 80 | #search-field-container { 81 | display: flex; 82 | flex-direction: row; 83 | flex-wrap: nowrap; 84 | margin: var(--bounding-margin) var(--bounding-margin) 0.65em; 85 | padding: 0; 86 | } 87 | 88 | #search-field-and-search-button { 89 | display: flex; 90 | flex-direction: row; 91 | flex-grow: 1; 92 | margin-inline-end: 0.5em; 93 | position: relative; 94 | } 95 | 96 | #search-field { 97 | flex-grow: 1; 98 | margin: 0; 99 | min-width: 10em; 100 | } 101 | :root:not([theme="default"]) #search-field { 102 | background: var(--search-field-bgcolor); 103 | border: 1px solid var(--search-field-bordercolor); 104 | border-radius: 0.2em; 105 | color: var(--search-field-textcolor); 106 | padding: 0.3em 0.5em; 107 | } 108 | 109 | #do-search-button { 110 | bottom: 1px; 111 | display: none; 112 | position: absolute; 113 | inset-inline-end: 1px; 114 | top: 1px; 115 | } 116 | #search-field.pasted ~ #do-search-button { 117 | display: inline; 118 | } 119 | 120 | 121 | .search-engines-switcher { 122 | border-radius: 0.2em; 123 | opacity: 0.35; 124 | padding: 0.25em; 125 | } 126 | 127 | :root.by-name #switch-to-recently-used, 128 | :root:not(.by-name) #switch-to-all { 129 | opacity: 0.75; 130 | } 131 | :root.by-name #switch-to-recently-used:hover, 132 | :root:not(.by-name) #switch-to-all:hover { 133 | background: var(--focused-item-bgcolor); 134 | color: var(--focused-item-textcolor); 135 | opacity: 1; 136 | } 137 | 138 | #switch-to-recently-used::before { 139 | content: "<"; 140 | } 141 | #switch-to-all::before { 142 | content: ">"; 143 | } 144 | 145 | #search-engines-container { 146 | display: flex; 147 | flex-direction: column; 148 | flex-wrap: nowrap; 149 | margin: 0 0 var(--bounding-margin); 150 | overflow: hidden; 151 | position: relative; 152 | } 153 | 154 | :root.no-engine #search-engines-container { 155 | display: none; 156 | } 157 | 158 | :root.building #search-engines-container { 159 | visibility: hidden; 160 | } 161 | 162 | .search-engines { 163 | display: flex; 164 | flex-direction: column; 165 | flex-wrap: nowrap; 166 | list-style: none; 167 | margin: 0; 168 | max-height: 15em; 169 | max-width: var(--panel-width, 100%); 170 | overflow-x: hidden; 171 | overflow-y: auto; 172 | padding: 0; 173 | scrollbar-width: thin; 174 | transition: inset-inline-start ease 0.15s, 175 | opacity ease 0.15s; 176 | width: var(--panel-width); 177 | } 178 | 179 | @media (prefers-reduced-motion: reduce) { 180 | .search-engines { 181 | transition: none; 182 | } 183 | } 184 | 185 | 186 | :root.by-name .search-engines#search-engines-by-recently-used { 187 | opacity: 0; 188 | pointer-events: none; 189 | } 190 | 191 | .search-engines#search-engines-by-name { 192 | inset-inline-start: var(--panel-width, 200%); 193 | opacity: 0; 194 | position: absolute; 195 | } 196 | :root.by-name .search-engines#search-engines-by-name { 197 | inset-inline-start: 0; 198 | opacity: 1; 199 | } 200 | 201 | .search-engines li { 202 | display: flex; 203 | flex-direction: row; 204 | flex-wrap: nowrap; 205 | margin: 0; 206 | padding-block: 0.25em; 207 | padding-inline: var(--bounding-margin) calc(var(--bounding-margin) + 0.5em); /* right padding should be enough large to show scrollbar */ 208 | } 209 | 210 | .search-engines li.active { 211 | background: var(--focused-item-bgcolor); 212 | color: var(--focused-item-textcolor); 213 | 214 | /* This is for the high contrast mode. The color will be overriden with the text color on the mode so it will provide better visibility for the active item. */ 215 | text-decoration: underline; 216 | text-decoration-color: transparent; 217 | } 218 | 219 | .favicon { 220 | max-height: 16px; 221 | max-width: 16px; 222 | min-width: 16px; 223 | } 224 | 225 | .label { 226 | flex-grow: 1; 227 | margin-inline-start: 0.25em; 228 | white-space: nowrap; 229 | } 230 | 231 | 232 | label[disabled="true"] { 233 | opacity: 0.5; 234 | } 235 | label[disabled="true"] input { 236 | opacity: 1; 237 | } 238 | 239 | 240 | input:focus { 241 | border-color: var(--in-content-border-active); 242 | box-shadow: none; 243 | } 244 | -------------------------------------------------------------------------------- /webextensions/panel/panel.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 22 | 27 | 28 | 29 | 30 | 34 | 38 |
39 |
40 |

__MSG_howToAdd_description__

41 | 46 |
47 |
48 |
    52 |
      56 |
      57 | 58 | 59 | -------------------------------------------------------------------------------- /webextensions/panel/panel.js: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 'use strict'; 7 | 8 | import Scroll from '/extlib/scroll.js'; 9 | 10 | /* global gLogContext, configs */ 11 | /* global kCOMMAND_DO_SEARCH, kCOMMAND_GET_SEARCH_ENGINES */ 12 | /* global kOPEN_IN_CURRENT, kOPEN_IN_TAB, kOPEN_IN_BACKGROUND_TAB, kOPEN_IN_WINDOW */ 13 | 14 | gLogContext = 'Panel'; 15 | 16 | let gStyleVariables; 17 | let gField; 18 | let gHistory; 19 | let gSearchButton; 20 | let gContainer; 21 | let gRecentlyUsedEngines; 22 | let gAllEngines; 23 | let gActiveEngines; 24 | let gEnginesSwitchers; 25 | let gPageSelection; 26 | let gCurrentTab; 27 | 28 | window.addEventListener('DOMContentLoaded', async () => { 29 | gStyleVariables = document.querySelector('#variables'); 30 | 31 | gField = document.querySelector('#search-field'); 32 | gHistory = document.querySelector('#search-history'); 33 | gSearchButton = document.querySelector('#do-search-button'); 34 | gContainer = document.querySelector('#search-engines-container'); 35 | 36 | gRecentlyUsedEngines = document.querySelector('#search-engines-by-recently-used'); 37 | gAllEngines = document.querySelector('#search-engines-by-name'); 38 | 39 | gRecentlyUsedEngines.scroll = new Scroll(gRecentlyUsedEngines, { 40 | duration: configs.smoothScrollDuration 41 | }); 42 | gAllEngines.scroll = new Scroll(gAllEngines, { 43 | duration: configs.smoothScrollDuration 44 | }); 45 | 46 | gEnginesSwitchers = { 47 | toRecentlyUsed: document.querySelector('#switch-to-recently-used'), 48 | toAll: document.querySelector('#switch-to-all') 49 | }; 50 | switchToRecentlyUsedEngines(); 51 | 52 | document.documentElement.classList.toggle('rtl', isRTL()); 53 | }, { once: true }); 54 | 55 | configs.$loaded.then(() => { 56 | document.documentElement.dataset.theme = configs.theme; 57 | for (const term of configs.history) { 58 | const item = document.createElement('option'); 59 | item.setAttribute('value', term); 60 | gHistory.appendChild(item); 61 | } 62 | }); 63 | 64 | window.addEventListener('pageshow', async () => { 65 | document.addEventListener('paste', onPaste); 66 | document.addEventListener('submit', onSubmit, { capture: true }); 67 | document.addEventListener('keydown', onKeyDown, { capture: true }); 68 | gField.addEventListener('compositionstart', onComposition, { capture: true }); 69 | gField.addEventListener('compositionupdate', onComposition, { capture: true }); 70 | gField.addEventListener('compositionend', onComposition, { capture: true }); 71 | gField.addEventListener('input', onInput); 72 | gContainer.addEventListener('mouseup', onEngineClick, { capture: true }); 73 | gContainer.addEventListener('mousemove', onMouseMove); 74 | gField.parentNode.addEventListener('mousemove', onMouseMove); 75 | gEnginesSwitchers.toRecentlyUsed.addEventListener('click', onSwitcherClick, { capture: true }); 76 | gEnginesSwitchers.toAll.addEventListener('click', onSwitcherClick, { capture: true }); 77 | gSearchButton.addEventListener('click', onSearchButtonClick); 78 | 79 | document.documentElement.classList.add('building'); 80 | 81 | if (configs.autocomplete) { 82 | gField.setAttribute('autocomplete', 'on'); 83 | gField.setAttribute('list', gHistory.id); 84 | } 85 | else { 86 | gField.setAttribute('autocomplete', 'off'); 87 | gField.removeAttribute('list'); 88 | } 89 | 90 | await configs.$loaded; 91 | 92 | if (configs.clearFieldAfterSearch && 93 | configs.lastSearchTime >= 0 && 94 | Date.now() - configs.lastSearchTime > configs.clearFieldAfterSearchDelay) { 95 | gField.value = ''; 96 | } 97 | else { 98 | gField.value = configs.lastSearchTerm; 99 | } 100 | 101 | gPageSelection = null; 102 | gCurrentTab = null; 103 | 104 | await Promise.all([ 105 | (async () => { 106 | const recentlyUsedEngines = await browser.runtime.sendMessage({ type: kCOMMAND_GET_SEARCH_ENGINES }); 107 | if (recentlyUsedEngines.length == 0) { 108 | /* 109 | Permissions.initUI({ 110 | checkbox: document.getElementById('searchPermission'), 111 | permission: Permissions.SEARCH_PERMISSION, 112 | onChange() { 113 | configs.cachedEnginesById = null; 114 | location.reload(); 115 | } 116 | }); 117 | */ 118 | document.documentElement.classList.add('no-engine'); 119 | } 120 | else { 121 | document.documentElement.classList.remove('no-engine'); 122 | buildEngines(recentlyUsedEngines, gRecentlyUsedEngines); 123 | buildEngines(recentlyUsedEngines.sort((aA, aB) => aA.title > aB.title), gAllEngines); 124 | } 125 | })(), 126 | updateUIForCurrentTab() 127 | ]); 128 | 129 | gStyleVariables.textContent = `:root { 130 | --panel-width: ${gContainer.offsetWidth}px; 131 | }`; 132 | 133 | document.documentElement.classList.remove('building'); 134 | focusToField(); 135 | }, { once: true }); 136 | 137 | window.addEventListener('pagehide', () => { 138 | document.removeEventListener('paste', onPaste); 139 | document.removeEventListener('submit', onSubmit, { capture: true }); 140 | document.removeEventListener('keydown', onKeyDown, { capture: true }); 141 | gField.removeEventListener('compositionstart', onComposition, { capture: true }); 142 | gField.removeEventListener('compositionupdate', onComposition, { capture: true }); 143 | gField.removeEventListener('compositionend', onComposition, { capture: true }); 144 | gField.removeEventListener('input', onInput); 145 | gContainer.removeEventListener('mouseup', onEngineClick, { capture: true }); 146 | gContainer.removeEventListener('mousemove', onMouseMove); 147 | gField.parentNode.removeEventListener('mousemove', onMouseMove); 148 | gEnginesSwitchers.toRecentlyUsed.removeEventListener('click', onSwitcherClick, { capture: true }); 149 | gEnginesSwitchers.toAll.removeEventListener('click', onSwitcherClick, { capture: true }); 150 | gSearchButton.removeEventListener('click', onSearchButtonClick); 151 | }, { once: true }); 152 | 153 | 154 | function onPaste(_event) { 155 | gField.classList.add('pasted'); 156 | } 157 | 158 | function onComposition(_event) { 159 | gField.classList.remove('pasted'); 160 | } 161 | 162 | let gLastEnterEvent; 163 | 164 | function onSubmit(event) { 165 | event.preventDefault(); 166 | event.stopPropagation(); 167 | doSearch({ 168 | ...searchParamsFromEvent(gLastEnterEvent), 169 | save: true, 170 | engine: getActiveEngine() 171 | }); 172 | gLastEnterEvent = null; 173 | return false; 174 | } 175 | 176 | function onKeyDown(event) { 177 | if (event.isComposing) 178 | return; 179 | 180 | gField.classList.remove('pasted'); 181 | if (event.key == 'Enter') { 182 | gLastEnterEvent = event; 183 | return; 184 | } 185 | gLastEnterEvent = null; 186 | 187 | const noModifiers = ( 188 | !event.altKey && 189 | !event.ctrlKey && 190 | !event.shiftKey && 191 | !event.metaKey 192 | ); 193 | const activeItem = getActiveEngine(); 194 | switch (event.key) { 195 | case 'Escape': 196 | if (noModifiers) 197 | window.close(); 198 | return; 199 | 200 | case 'ArrowLeft': 201 | if (!event.altKey && 202 | (event.ctrlKey || event.metaKey) && 203 | !event.shiftKey) { 204 | if (isRTL()) 205 | switchToAllEngines(); 206 | else 207 | switchToRecentlyUsedEngines(); 208 | } 209 | return; 210 | 211 | case 'ArrowRight': 212 | if (!event.altKey && 213 | (event.ctrlKey || event.metaKey) && 214 | !event.shiftKey) { 215 | if (isRTL()) 216 | switchToRecentlyUsedEngines(); 217 | else 218 | switchToAllEngines(); 219 | } 220 | return; 221 | 222 | case 'ArrowUp': 223 | if (!noModifiers) 224 | return; 225 | if (activeItem) { 226 | activeItem.classList.remove('active'); 227 | activeItem.setAttribute('aria-selected', 'false'); 228 | const item = activeItem.previousSibling; 229 | if (item) { 230 | item.classList.add('active'); 231 | item.setAttribute('aria-selected', 'true'); 232 | item.parentNode.setAttribute('aria-activedescendant', item.id); 233 | gActiveEngines.scroll.scrollToItem(item); 234 | } 235 | } 236 | else if (gActiveEngines.hasChildNodes()) { 237 | gActiveEngines.lastChild.classList.add('active'); 238 | gActiveEngines.lastChild.setAttribute('aria-selected', 'true'); 239 | gActiveEngines.scroll.scrollToItem(gActiveEngines.lastChild); 240 | } 241 | event.stopImmediatePropagation(); 242 | event.preventDefault(); 243 | return; 244 | 245 | case 'ArrowDown': 246 | if (!noModifiers) 247 | return; 248 | if (activeItem) { 249 | activeItem.classList.remove('active'); 250 | activeItem.setAttribute('aria-selected', 'false'); 251 | const item = activeItem.nextSibling; 252 | if (item) { 253 | item.classList.add('active'); 254 | item.setAttribute('aria-selected', 'true'); 255 | gActiveEngines.scroll.scrollToItem(item); 256 | } 257 | } 258 | else if (gActiveEngines.hasChildNodes()) { 259 | gActiveEngines.firstChild.classList.add('active'); 260 | gActiveEngines.scroll.scrollToItem(gActiveEngines.firstChild); 261 | } 262 | event.stopImmediatePropagation(); 263 | event.preventDefault(); 264 | return; 265 | } 266 | } 267 | 268 | function onInput(event) { 269 | if (event.isComposing) 270 | return; 271 | 272 | const oldActive = getActiveEngine(); 273 | if (oldActive && configs.clearFocusByInput) { 274 | oldActive.classList.remove('active'); 275 | oldActive.setAttribute('aria-selected', 'false'); 276 | } 277 | 278 | configs.lastSearchTerm = gField.value; 279 | configs.lastSearchTime = -1; 280 | } 281 | 282 | function searchParamsFromEvent(event) { 283 | const searchParams = { 284 | where: configs.defaultOpenIn, 285 | keepOpen: false 286 | }; 287 | if (event && (event.altKey || event.ctrlKey || event.metaKey)) { 288 | searchParams.where = configs.accelActionOpenIn; 289 | searchParams.keepOpen = configs.accelActionOpenIn == kOPEN_IN_BACKGROUND_TAB; 290 | return searchParams; 291 | } 292 | 293 | if (event && event.shiftKey) { 294 | searchParams.where = kOPEN_IN_WINDOW; 295 | return searchParams; 296 | } 297 | 298 | if (configs.recycleBlankCurrentTab && 299 | configs.defaultOpenIn == kOPEN_IN_TAB && 300 | (gCurrentTab.url == 'about:blank' || 301 | (configs.recycleTabUrlPattern && 302 | new RegExp(configs.recycleTabUrlPattern).test(gCurrentTab.url)))) { 303 | searchParams.where = kOPEN_IN_CURRENT; 304 | return searchParams; 305 | } 306 | 307 | return searchParams; 308 | } 309 | 310 | function onEngineClick(event) { 311 | let engine = event.target; 312 | while (engine.nodeType != Node.ELEMENT_NODE || 313 | !engine.hasAttribute('data-id')) { 314 | engine = engine.parentNode; 315 | if (!engine) 316 | return; 317 | } 318 | switch (event.button) { 319 | case 0: 320 | doSearch({ 321 | ...searchParamsFromEvent(event), 322 | engine 323 | }); 324 | break; 325 | 326 | case 1: 327 | doSearch({ 328 | where: configs.accelActionOpenIn, 329 | keepOpen: configs.accelActionOpenIn == kOPEN_IN_BACKGROUND_TAB, 330 | engine 331 | }); 332 | break; 333 | 334 | default: 335 | break; 336 | } 337 | } 338 | 339 | function onSwitcherClick(event) { 340 | switch (event.currentTarget.id) { 341 | case 'switch-to-recently-used': 342 | switchToRecentlyUsedEngines(); 343 | gField.focus(); 344 | return; 345 | 346 | case 'switch-to-all': 347 | switchToAllEngines(); 348 | gField.focus(); 349 | return; 350 | 351 | default: 352 | return; 353 | } 354 | } 355 | 356 | function onSearchButtonClick(event) { 357 | doSearch(searchParamsFromEvent(event)); 358 | gField.classList.remove('pasted'); 359 | } 360 | 361 | function onMouseMove(event) { 362 | const oldActive = getActiveEngine(); 363 | if (oldActive) { 364 | oldActive.classList.remove('active'); 365 | oldActive.setAttribute('aria-selected', 'false'); 366 | } 367 | 368 | const hoverEngine = event.target.closest('.search-engines li'); 369 | if (hoverEngine) { 370 | hoverEngine.classList.add('active'); 371 | hoverEngine.setAttribute('aria-selected', 'true'); 372 | } 373 | } 374 | 375 | function getActiveEngine() { 376 | return gActiveEngines.querySelector('li.active'); 377 | } 378 | 379 | function switchToRecentlyUsedEngines() { 380 | document.documentElement.classList.remove('by-name'); 381 | gEnginesSwitchers.toRecentlyUsed.setAttribute('aria-disabled', 'true'); 382 | gEnginesSwitchers.toAll.setAttribute('aria-disabled', 'false'); 383 | gActiveEngines = gRecentlyUsedEngines; 384 | gActiveEngines.scroll.scrollTo({ position: 0, justNow: true }); 385 | } 386 | 387 | function switchToAllEngines() { 388 | document.documentElement.classList.add('by-name'); 389 | gEnginesSwitchers.toAll.setAttribute('aria-disabled', 'true'); 390 | gEnginesSwitchers.toRecentlyUsed.setAttribute('aria-disabled', 'false'); 391 | gActiveEngines = gAllEngines; 392 | gActiveEngines.scroll.scrollTo({ position: 0, justNow: true }); 393 | } 394 | 395 | async function updateUIForCurrentTab() { 396 | try { 397 | gCurrentTab = (await browser.tabs.query({ 398 | currentWindow: true, 399 | active: true 400 | }))[0]; 401 | if (!configs.fillFieldWithSelectionText) 402 | return; 403 | const selections = await browser.tabs.executeScript(gCurrentTab.id, { 404 | code: `(() => { 405 | const focused = document.hasFocus(); 406 | 407 | let selection = window.getSelection(); 408 | if (selection.rangeCount > 0) { 409 | let selectionText = selection.toString().trim(); 410 | if (selectionText != '') 411 | return { selection: selectionText, focused }; 412 | } 413 | 414 | let field = document.activeElement; 415 | if (!field || !field.matches('input, textarea')) 416 | return { selection: '', focused }; 417 | 418 | let selectionText = (field.value || '').substring(field.selectionStart || 0, field.selectionEnd || 0); 419 | return { selection: selectionText.trim(), focused }; 420 | })();`, 421 | allFrames: true 422 | }); 423 | const activeSelection = selections.filter(aSelection => aSelection.focused)[0] || selections[0]; 424 | gPageSelection = activeSelection.selection.trim(); 425 | if (gPageSelection != '') { 426 | gField.value = gPageSelection; 427 | gField.select(); 428 | } 429 | if (gField.value != '') 430 | gField.classList.add('pasted'); 431 | } 432 | catch(_error) { 433 | // if it is a special tab, we cannot execute script. 434 | //console.log(error); 435 | } 436 | } 437 | 438 | function focusToField() { 439 | window.focus(); 440 | setTimeout(() => { 441 | gField.focus(); 442 | if (configs.lastSearchTime > 0) 443 | gField.select(); 444 | }, configs.focusDelay); 445 | } 446 | 447 | function buildEngines(aEngines, aContainer) { 448 | const items = document.createDocumentFragment(); 449 | for (const engine of aEngines) { 450 | const item = document.createElement('li'); 451 | item.id = `${aContainer.id}:engine:${engine.id}`; 452 | item.setAttribute('data-id', engine.id); 453 | item.setAttribute('data-url', engine.url); 454 | item.setAttribute('role', 'option'); 455 | item.setAttribute('aria-selected', 'false'); 456 | 457 | const favicon = document.createElement('img'); 458 | favicon.classList.add('favicon'); 459 | if (engine.favIconUrl) 460 | favicon.setAttribute('src', engine.favIconUrl); 461 | item.appendChild(favicon); 462 | 463 | const label = document.createElement('span'); 464 | label.classList.add('label'); 465 | label.textContent = engine.title; 466 | item.appendChild(label); 467 | 468 | items.appendChild(item); 469 | } 470 | aContainer.appendChild(items); 471 | } 472 | 473 | async function doSearch(aParams = {}) { 474 | 475 | // I don't know why but both "click" and "submit" events are fired when 476 | // I hit the Enter key with highlighted search engine. So we need to 477 | // prevent multiple times doSearch()ing. 478 | if (doSearch.done) 479 | return; 480 | 481 | doSearch.done = true; 482 | 483 | const item = aParams.engine || getActiveEngine(); 484 | const term = gField.value.trim(); 485 | if (term) 486 | addHistory(term); 487 | 488 | await browser.runtime.sendMessage({ 489 | type: kCOMMAND_DO_SEARCH, 490 | engineId: item && item.getAttribute('data-id'), 491 | where: aParams.where, 492 | term, 493 | tabId: gCurrentTab.id, 494 | openerTabId: gPageSelection && gPageSelection == gField.value ? gCurrentTab.id : null, 495 | save: !!(item && (aParams.save || gField.value)) 496 | }); 497 | 498 | if (!configs.closeAfterSearch || aParams.keepOpen) 499 | return; 500 | 501 | configs.lastSearchTerm = gField.value; 502 | configs.lastSearchTime = Date.now(); 503 | 504 | window.close(); 505 | } 506 | 507 | function addHistory(aTerm) { 508 | let history = configs.history; 509 | const index = history.indexOf(aTerm); 510 | if (index > -1) { 511 | history.splice(index, 1); 512 | const item = gHistory.querySelector(`option[value=${JSON.stringify(aTerm)}]`); 513 | if (item) 514 | gHistory.removeChild(item); 515 | } 516 | history.unshift(aTerm); 517 | const item = document.createElement('option'); 518 | item.setAttribute('value', aTerm); 519 | gHistory.insertBefore(item, gHistory.firstChild); 520 | history = history.slice(0, configs.maxHistoryCount); 521 | configs.history = history; 522 | } 523 | -------------------------------------------------------------------------------- /webextensions/resources/16x16.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 35 | 36 | 41 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /webextensions/resources/32x32.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 35 | 36 | 43 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /webextensions/resources/ui-base.css: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | /* 8 | Global styles for extensions page from chrome://browser/content/extension.css 9 | They are applied to sidebar contents automatically but not provided for 10 | in-page contents, so we manually define them here. 11 | */ 12 | html, 13 | body { 14 | box-sizing: border-box; 15 | cursor: default; 16 | display: flex; 17 | flex-direction: column; 18 | font: caption; 19 | font-weight: normal; /* This is required to cancel unexpected bold weight produced by the "caption" system font name on Windows 7 Classic theme. See also: https://github.com/piroor/treestyletab/issues/2636 */ 20 | margin: 0; 21 | padding: 0; 22 | -moz-user-select: none; 23 | } 24 | body * { 25 | box-sizing: border-box; 26 | text-align: start; 27 | } 28 | -------------------------------------------------------------------------------- /webextensions/resources/ui-color.css: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | :root { 8 | /* https://hg.mozilla.org/mozilla-central/raw-file/tip/toolkit/themes/shared/in-content/common.inc.css */ 9 | --in-content-page-color: var(--grey-90); 10 | --in-content-page-background: var(--grey-10); 11 | --in-content-text-color: var(--in-content-page-color); 12 | --in-content-deemphasized-text: var(--grey-60); 13 | --in-content-selected-text: #fff; 14 | --in-content-box-background: #fff; 15 | --in-content-box-background-hover: var(--grey-20); 16 | --in-content-box-background-active: var(--grey-30); 17 | --in-content-box-border-color: var(--grey-90-a30); 18 | --in-content-box-border-color-mixed: rgb(calc((249 * 0.7) + (12 * 0.3)), calc((249 * 0.7) + (12 * 0.3)), calc((250 * 0.7) + (13 * 0.3))); 19 | --in-content-box-info-background: var(--grey-20); 20 | --in-content-item-hover: rgba(69, 161, 255, 0.2); /* blue 40 a20 */ 21 | --in-content-item-hover-mixed: rgb(calc((249 * 0.8) + (69 * 0.2)), calc((249 * 0.8) + (161 * 0.2)), calc((250 * 0.8) + (255 * 0.2))); 22 | --in-content-item-selected: var(--blue-50); 23 | --in-content-border-highlight: var(--blue-50); 24 | --in-content-border-focus: var(--blue-50); 25 | --in-content-border-hover: var(--grey-90-a50); 26 | --in-content-border-hover-mixed: rgb(calc((249 * 0.5) + (12 * 0.5)), calc((249 * 0.5) + (12 * 0.5)), calc((250 * 0.5) + (13 * 0.5))); 27 | --in-content-border-active: var(--blue-50); 28 | --in-content-border-active-shadow: var(--blue-50-a30); 29 | --in-content-border-invalid: var(--red-50); 30 | --in-content-border-invalid-shadow: var(--red-50-a30); 31 | --in-content-border-color: var(--grey-30); 32 | --in-content-border-color-mixed: #d7d7db; 33 | --in-content-category-outline-focus: 1px dotted var(--blue-50); 34 | --in-content-category-text-selected: var(--blue-50); 35 | --in-content-category-text-selected-active: var(--blue-60); 36 | --in-content-category-background-hover: rgba(12,12,13,0.1); 37 | --in-content-category-background-active: rgba(12,12,13,0.15); 38 | --in-content-category-background-selected-hover: rgba(12,12,13,0.15); 39 | --in-content-category-background-selected-active: rgba(12,12,13,0.2); 40 | --in-content-tab-color: #424f5a; 41 | --in-content-link-color: var(--blue-60); 42 | --in-content-link-color-hover: var(--blue-70); 43 | --in-content-link-color-active: var(--blue-80); 44 | --in-content-link-color-visited: var(--blue-60); 45 | --in-content-button-background: var(--grey-90-a10); 46 | --in-content-button-background-mixed: rgb(calc((249 * 0.9) + (12 * 0.1)), calc((249 * 0.9) + (12 * 0.1)), calc((250 * 0.9) + (13 * 0.1))); 47 | --in-content-button-background-hover: var(--grey-90-a20); 48 | --in-content-button-background-hover-mixed: rgb(calc((249 * 0.8) + (12 * 0.2)), calc((249 * 0.8) + (12 * 0.2)), calc((250 * 0.8) + (13 * 0.2))); 49 | --in-content-button-background-active: var(--grey-90-a30); 50 | --in-content-button-background-active-mixed: rgb(calc((249 * 0.7) + (12 * 0.3)), calc((249 * 0.7) + (12 * 0.3)), calc((250 * 0.7) + (13 * 0.3))); 51 | 52 | --blue-40: #45a1ff; 53 | --blue-40-a10: rgb(69, 161, 255, 0.1); 54 | --blue-50: #0a84ff; 55 | --blue-50-a30: rgba(10, 132, 255, 0.3); 56 | --blue-60: #0060df; 57 | --blue-70: #003eaa; 58 | --blue-80: #002275; 59 | --grey-10: #f9f9fa; 60 | --grey-10-a015: rgba(249, 249, 250, 0.015); 61 | --grey-10-a20: rgba(249, 249, 250, 0.2); 62 | --grey-10-a85: rgba(249, 249, 250, 0.85); 63 | --grey-20: #ededf0; 64 | --grey-30: #d7d7db; 65 | --grey-40: #b1b1b3; 66 | --grey-60: #4a4a4f; 67 | --grey-90: #0c0c0d; 68 | --grey-90-a10: rgba(12, 12, 13, 0.1); 69 | --grey-90-a20: rgba(12, 12, 13, 0.2); 70 | --grey-90-a30: rgba(12, 12, 13, 0.3); 71 | --grey-90-a50: rgba(12, 12, 13, 0.5); 72 | --grey-90-a60: rgba(12, 12, 13, 0.6); 73 | --green-50: #30e60b; 74 | --green-60: #12bc00; 75 | --green-70: #058b00; 76 | --green-80: #006504; 77 | --green-90: #003706; 78 | --orange-50: #ff9400; 79 | --purple-70: #6200a4; 80 | --red-50: #ff0039; 81 | --red-50-a10: rgba(255, 0, 57, 0.1); 82 | --red-50-a30: rgba(255, 0, 57, 0.3); 83 | --red-60: #d70022; 84 | --red-70: #a4000f; 85 | --red-80: #5a0002; 86 | --red-90: #3e0200; 87 | --yellow-10: #ffff98; 88 | --yellow-50: #ffe900; 89 | --yellow-60: #d7b600; 90 | --yellow-60-a30: rgba(215, 182, 0, 0.3); 91 | --yellow-70: #a47f00; 92 | --yellow-80: #715100; 93 | --yellow-90: #3e2800; 94 | 95 | /* https://hg.mozilla.org/mozilla-central/raw-file/tip/browser/themes/addons/dark/manifest.json */ 96 | --dark-frame: hsl(240, 5%, 5%); 97 | --dark-icons: rgb(249, 249, 250, 0.7); 98 | --dark-ntp-background: #2A2A2E; 99 | --dark-ntp-text: rgb(249, 249, 250); 100 | --dark-popup: #4a4a4f; 101 | --dark-popup-border: #27272b; 102 | --dark-popup-text: rgb(249, 249, 250); 103 | --dark-sidebar: #38383D; 104 | --dark-sidebar-text: rgb(249, 249, 250); 105 | --dark-sidebar-border: rgba(255, 255, 255, 0.1); 106 | --dark-tab-background-text: rgb(249, 249, 250); 107 | --dark-tab-line: #0a84ff; 108 | --dark-toolbar: hsl(240, 1%, 20%); 109 | --dark-toolbar-bottom-separator: hsl(240, 5%, 5%); 110 | --dark-toolbar-field: rgb(71, 71, 73); 111 | --dark-toolbar-field-border: rgba(249, 249, 250, 0.2); 112 | --dark-toolbar-field-separator: #5F6670; 113 | --dark-toolbar-field-text: rgb(249, 249, 250); 114 | 115 | /* https://searchfox.org/mozilla-central/rev/35873cfc312a6285f54aa5e4ec2d4ab911157522/browser/themes/shared/tabs.inc.css#24 */ 116 | --tab-loading-fill: #0A84FF; 117 | 118 | 119 | --bg-color: var(--grey-10); 120 | --text-color: var(--grey-90); 121 | } 122 | 123 | :link { 124 | color: var(--in-content-link-color); 125 | } 126 | :visited { 127 | color: var(--in-content-link-color-visited); 128 | } 129 | 130 | :link:hover, 131 | :visited:hover { 132 | color: var(--in-content-link-color-hover); 133 | } 134 | 135 | :link:active, 136 | :visited:active { 137 | color: var(--in-content-link-color-active); 138 | } 139 | 140 | textarea, 141 | input { 142 | background: var(--in-content-box-background); 143 | border: thin solid var(--in-content-box-border-color-mixed); 144 | color: var(--in-content-text-color); 145 | } 146 | textarea:hover, 147 | input:hover { 148 | border-color: var(--in-content-border-hover-mixed); 149 | } 150 | textarea:focus, 151 | input:focus { 152 | border-color: var(--in-content-border-focus); 153 | box-shadow: 0 0 0 1px var(--in-content-border-active), 154 | 0 0 0 4px var(--in-content-border-active-shadow); 155 | } 156 | button, 157 | select { 158 | background: var(--in-content-button-background-mixed); 159 | border: 0 none transparent; 160 | color: var(--in-content-text-color); 161 | } 162 | button:hover, 163 | select:hover { 164 | background: var(--in-content-button-background-hover-mixed); 165 | } 166 | button:focus, 167 | select:focus { 168 | background: var(--in-content-button-background-active-mixed); 169 | box-shadow: 0 0 0 1px var(--in-content-border-active), 170 | 0 0 0 4px var(--in-content-border-active-shadow); 171 | } 172 | option { 173 | background: var(--bg-color); 174 | color: var(--text-color); 175 | } 176 | option:active, 177 | option:focus { 178 | background: var(--in-content-item-selected); 179 | } 180 | option:hover { 181 | background: var(--in-content-item-hover-mixed); 182 | } 183 | 184 | fieldset, 185 | hr { 186 | border: thin solid var(--in-content-box-border-color-mixed); 187 | } 188 | 189 | hr { 190 | border-width: thin 0 0 0; 191 | } 192 | 193 | @media (prefers-color-scheme: dark) { 194 | :root { 195 | /* https://hg.mozilla.org/mozilla-central/raw-file/tip/toolkit/themes/shared/in-content/common.inc.css */ 196 | --in-content-page-background: #2A2A2E /* rgb(42, 42, 46) */; 197 | --in-content-page-color: rgb(249, 249, 250); 198 | --in-content-text-color: var(--in-content-page-color); 199 | --in-content-deemphasized-text: var(--grey-40); 200 | --in-content-box-background: #202023; 201 | --in-content-box-background-hover: /* rgba(249,249,250,0.15) */ rgb(calc((42 * 0.85) + (249 * 0.15)), calc((42 * 0.85) + (249 * 0.15)), calc((46 * 0.85) + (250 * 0.15))); 202 | --in-content-box-background-active: /*rgba(249,249,250,0.2) */ rgb(calc((42 * 0.8) + (249 * 0.2)), calc((42 * 0.8) + (249 * 0.2)), calc((46 * 0.8) + (250 * 0.2))); 203 | --in-content-box-background-odd: rgba(249,249,250,0.05); 204 | --in-content-box-info-background: rgba(249,249,250,0.15); 205 | 206 | --in-content-border-color: rgba(249,249,250,0.2); 207 | --in-content-border-color-mixed: rgb(calc((42 * 0.8) + (249 * 0.2)), calc((42 * 0.8) + (249 * 0.2)), calc((46 * 0.8) + (250 * 0.2))); 208 | --in-content-border-hover: rgba(249,249,250,0.3); 209 | --in-content-border-hover-mixed: rgb(calc((42 * 0.7) + (249 * 0.3)), calc((42 * 0.7) + (249 * 0.3)), calc((46 * 0.7) + (250 * 0.3))); 210 | --in-content-box-border-color: rgba(249,249,250,0.2); 211 | --in-content-box-border-color-mixed: rgb(calc((42 * 0.8) + (249 * 0.2)), calc((42 * 0.8) + (249 * 0.2)), calc((46 * 0.8) + (250 * 0.2))); 212 | 213 | --in-content-button-background: rgba(249,249,250,0.1); 214 | --in-content-button-background-mixed: rgb(calc((42 * 0.9) + (249 * 0.1)), calc((42 * 0.9) + (249 * 0.1)), calc((46 * 0.9) + (250 * 0.1))); 215 | --in-content-button-background-hover: rgba(249,249,250,0.15); 216 | --in-content-button-background-hover-mixed: rgb(calc((42 * 0.85) + (249 * 0.15)), calc((42 * 0.85) + (249 * 0.15)), calc((46 * 0.85) + (250 * 0.15))); 217 | --in-content-button-background-active: rgba(249,249,250,0.2); 218 | --in-content-button-background-active-mixed: rgb(calc((42 * 0.8) + (249 * 0.2)), calc((42 * 0.8) + (249 * 0.2)), calc((46 * 0.8) + (250 * 0.2))); 219 | 220 | --in-content-link-color: var(--blue-40); 221 | --in-content-link-color-hover: var(--blue-50); 222 | --in-content-link-color-active: var(--blue-60); 223 | 224 | --bg-color: var(--in-content-page-background); 225 | --text-color: var(--in-content-text-color); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /webextensions/screenshots/add-bookmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piroor/secondsearch/ffb2349ffaac5a6c6f8b2571866f8048959b7579/webextensions/screenshots/add-bookmark.png -------------------------------------------------------------------------------- /webextensions/screenshots/add-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piroor/secondsearch/ffb2349ffaac5a6c6f8b2571866f8048959b7579/webextensions/screenshots/add-menu.png -------------------------------------------------------------------------------- /webextensions/screenshots/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piroor/secondsearch/ffb2349ffaac5a6c6f8b2571866f8048959b7579/webextensions/screenshots/main.png -------------------------------------------------------------------------------- /webextensions/tools/eslint/for-module.js: -------------------------------------------------------------------------------- 1 | /* 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | /*eslint-env commonjs*/ 7 | /*eslint quote-props: ['error', "always"] */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = { 12 | 'parserOptions': { 13 | 'sourceType': 'module', 14 | }, 15 | 16 | 'plugins': [ 17 | 'import', 18 | ], 19 | 20 | 'rules': { 21 | 'no-const-assign': 'error', 22 | 'prefer-const': ['warn', { 23 | 'destructuring': 'any', 24 | 'ignoreReadBeforeAssign': false 25 | }], 26 | 'no-var': 'error', 27 | 'no-unused-vars': ['warn', { // Not make an error for debugging. 28 | 'vars': 'all', 29 | 'args': 'after-used', 30 | 'argsIgnorePattern': '^_', 31 | 'caughtErrors': 'all', 32 | 'caughtErrorsIgnorePattern': '^_', // Allow `catch (_e) {...}` 33 | }], 34 | 'no-use-before-define': ['error', { // the measure for Temporary Dead Zone 35 | 'functions': false, // Function declarations are hoisted. 36 | 'classes': true, // Class declarations are not hoisted. We should warn it. 37 | }], 38 | 'no-unused-expressions': 'error', 39 | 'no-unused-labels': 'error', 40 | 'no-undef': ['error', { 41 | 'typeof': true, 42 | }], 43 | 44 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/default.md 45 | 'import/default': 'error', 46 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/namespace.md 47 | 'import/namespace': 'error', 48 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-duplicates.md 49 | 'import/no-duplicates': 'error', 50 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/export.md 51 | 'import/export': 'error', 52 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md 53 | 'import/extensions': ['error', 'always'], 54 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md 55 | 'import/first': 'error', 56 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/named.md 57 | 'import/named': 'error', 58 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-as-default.md 59 | 'import/no-named-as-default': 'error', 60 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-as-default-member.md 61 | 'import/no-named-as-default-member': 'error', 62 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-cycle.md 63 | 'import/no-cycle': ['warn', { 64 | // If we comment out this, `maxDepth` is `Infinity`. 65 | //'maxDepth': 1, 66 | }], 67 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md 68 | 'import/no-self-import': 'error', 69 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-unresolved.md 70 | 'import/no-unresolved': ['error', { 71 | 'caseSensitive': true, 72 | }], 73 | // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-useless-path-segments.md 74 | 'import/no-useless-path-segments': 'error', 75 | }, 76 | }; 77 | --------------------------------------------------------------------------------