Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
daleharvey committed Jan 9, 2020
0 parents commit 751a97e
Show file tree
Hide file tree
Showing 14 changed files with 7,774 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
15 changes: 15 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Community Participation Guidelines

This repository is governed by Mozilla's code of conduct and etiquette guidelines.
For more details, please read the
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).

## How to Report
For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.

<!--
## Project Specific Etiquette
In some cases, there will be additional project etiquette i.e.: (https://bugzilla.mozilla.org/page.cgi?id=etiquette.html).
Please update for your project.
-->
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# SearchEngine Devtools

This addon provides some tools to assist developers with
search engine configuration.

# Features

- Change locale and region
- Change configuration

![](screenshot.png)

# Install

- Pick the .xpi file from the [releases page](https://github.com/mozilla/searchengine-devtools/releases).
- When asked for comnfirmation, select "Continue to installation".

> Note: it is highly recommended to use a temporary or development user profile
# Development

```
npm install
```

Run in a browser with live-reload:

```
npm start -- --firefox-binary ~/path/to/firefox
```
5 changes: 5 additions & 0 deletions extension/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
browser.browserAction.onClicked.addListener(async () => {
await browser.tabs.create({
url: "content/index.html",
});
});
53 changes: 53 additions & 0 deletions extension/content/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!doctype html>
<html>

<head>
<meta charset="utf-8">
<title>Search Helper Extension</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="style.css" rel="stylesheet">
</head>

<body class="loading">

<section id="settings">
<div>
<label for="region-select">Region</label>
<select id="region-select"></select>
</div>

<div>
<label for="locale-select">Locale</label>
<select id="locale-select"></select>
</div>

<button id="reload-engines">Reload Engines</button>
<button id="reload-page">Clear Cache & Reload Page</button>

</section>

<section id="engines">
<table id="engines-table">
<thead>
<tr>
<th>Index</th>
<th>Id</th>
<th>Version</th>
<th>Locales</th>
<th>Params</th>
</tr>
</thead>
<tbody></tbody>
</table>
</section>

<section id="configuration">
<h2>Configuration:</h2>
<textarea id="config"></textarea>
</section>

</body>

<script type="text/javascript" src="script.js"></script>

</html>
100 changes: 100 additions & 0 deletions extension/content/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"use script";

const searchengines = browser.experiments.searchengines;
const $ = document.querySelector.bind(document);

const ONE_DAY = 1000 * 60 * 24;

const ENGINES_URL = "https://hg.mozilla.org/mozilla-central/raw-file/tip/browser/components/search/extensions/engines.json";
const LOCALES_URL ="https://hg.mozilla.org/mozilla-central/raw-file/tip/browser/locales/all-locales";

(async function main() {
await initUI();
await loadConfiguration();
await loadEngines();
document.body.classList.remove("loading");
})();

async function loadEngines() {
let locale = $("#locale-select").value;
let region = $("#region-select").value;
let config = "data:application/json;charset=UTF-8," + $("#config").value;
let { engines, private } =
await searchengines.getEngines(config, locale, region);
let list = engines.map((e, i) => `<tr data-id="${e.webExtension.id}">
<td>${i+1}</td>
<td>${e.webExtension.id}</td>
<td>${e.webExtension.version}</td>
<td>${e.webExtension.locales}</td>
<td class="params">${e.params ? JSON.stringify(e.params) : ""}</td>
</tr>`);
$("#engines-table tbody").innerHTML = list.join("");
}

async function initUI() {
let locales = await getLocales();
locales.unshift("default");
$("#locale-select").innerHTML =
locales.map(locale => `<option>${locale}</option>`);
$("#locale-select").value = (await searchengines.getCurrentLocale());

let regions = (await searchengines.getRegions())
.map(r => r.toUpperCase());
regions.unshift("default");
$("#region-select").innerHTML =
regions.map(region => `<option>${region}</option>`);
$("#region-select").value = (await searchengines.getCurrentRegion());

$("#reload-engines").addEventListener("click", reloadEngines);
$("#reload-page").addEventListener("click", reloadPage);

$("#engines-table tbody").addEventListener("click", showConfig);
}

async function loadConfiguration() {
let config = JSON.parse((await fetchCached(ENGINES_URL, ONE_DAY)));
$("#configuration textarea").value = JSON.stringify(config, null, 2);
}

async function showConfig(e) {
let row = e.target.closest("tr");
let id = row.dataset.id
if (!id) {
return;
}
let textarea = $("#config");
let line = config.value.split(id)[0].match(/\n/g).length;
var lineHeight = document.defaultView.getComputedStyle(textarea, null)
.getPropertyValue("line-height");
$("#config").scrollTop = line * parseInt(lineHeight, 10);
}

async function reloadEngines() {
document.body.classList.add("loading");
await loadEngines();
document.body.classList.remove("loading");
}

async function reloadPage() {
localStorage.clear();
location.reload(true);
}

async function getLocales() {
let data = await fetchCached(LOCALES_URL, ONE_DAY);
let locales = [...data.split("\n").filter(e => e != ""), "en-US"];
locales = locales.map(l => (l == "ja-JP-mac" ? "ja-JP-macos" : l));
return locales;
}

async function fetchCached(url, expiry) {
let cache = JSON.parse(localStorage.getItem(url));
if (cache && (Date.now() - expiry) < cache.time) {
console.log("using cache");
return cache.data;
}
let request = await fetch(url);
let data = await request.text();
localStorage.setItem(url, JSON.stringify({time: Date.now(), data}));
return data;
}
86 changes: 86 additions & 0 deletions extension/content/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
@import url("chrome://global/skin/in-content/common.css");

html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}

html, body {
max-height: 100vh;
max-width: 100vw;

--success-text-color: green;
--error-text-color: red;
--subtext-color: #888;
}

body {
margin: 40px;
color: var(--in-content-text-color);
}

body.loading {
opacity: 0.2;
}

section {
border: 1px solid var(--in-content-border-color);
background-color: var(--in-content-box-background);
padding: 10px;
word-break: break-all;
margin-bottom: 10px;
}

h1, h2, h3, h4 {
margin-top: 0;
}

#settings {
display: flex;
flex-direction: row;
}

#settings div {
margin-right: 1em;
}

table {
width: 100%;
border-collapse: collapse;
}

th {
padding-bottom: .5em;
}

tbody tr {
opacity: 0.6;
}
tbody tr:hover {
opacity: 0.9;
cursor: pointer;
}

tbody tr:nth-child(odd) {
background: #333;
}

td {
padding: .3em 1em;
border: 0;
}

.params {
max-width: 300px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

#configuration textarea {
width: 100%;
height: 250px;
line-height: 20px;
}
39 changes: 39 additions & 0 deletions extension/experiments/searchengines/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyModuleGetters(this, {
Services: "resource://gre/modules/Services.jsm",
SearchEngineSelector: "resource://gre/modules/SearchEngineSelector.jsm",
});

async function getCurrentRegion() {
return Services.prefs.getCharPref("browser.search.region", "default");
}

async function getRegions() {
return Services.intl.getAvailableLocaleDisplayNames("region");
}

async function getCurrentLocale() {
return Services.locale.appLocaleAsBCP47;
}

async function getEngines(configUrl, locale, region) {
let engineSelector = new SearchEngineSelector();
await engineSelector.init(configUrl);
return engineSelector.fetchEngineConfiguration(locale, region);
}

var searchengines = class extends ExtensionAPI {
getAPI(context) {
return {
experiments: {
searchengines: {
getCurrentLocale,
getRegions,
getCurrentRegion,
getEngines,
}
}
}
}
};
49 changes: 49 additions & 0 deletions extension/experiments/searchengines/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[
{
"namespace": "experiments.searchengines",
"description": "Search Engines",
"functions": [
{
"name": "getCurrentLocale",
"type": "function",
"description": "Return currently configured locale",
"async": true,
"parameters": []
},
{
"name": "getRegions",
"type": "function",
"description": "Return a list of regions firefox is aware of",
"async": true,
"parameters": []
},
{
"name": "getCurrentRegion",
"type": "function",
"description": "Return currently configured region",
"async": true,
"parameters": []
},
{
"name": "getEngines",
"type": "function",
"description": "Return the engine configuration for the given region, locale and configuration",
"async": true,
"parameters": [{
"name": "configUrl",
"description": "The configuration",
"type": "string"
}, {
"name": "locale",
"description": "The users locale",
"type": "string"
}, {
"name": "region",
"description": "The users region",
"type": "string"
}]
}
],
"events": []
}
]
Binary file added extension/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 751a97e

Please sign in to comment.