Improve search page popup detection (#1378)
* Add _getAllTabs function * Add _findTabs * Use _findTabs instead of _findTab * Remove _findTab * Refactor tab check * Add ability to search for a popup native window * Fix dangling comma
This commit is contained in:
parent
d5964ee4d4
commit
edc22b98e3
@ -737,7 +737,7 @@ class Backend {
|
|||||||
url += `?${queryString}`;
|
url += `?${queryString}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isTabMatch = (url2) => {
|
const predicate = ({url: url2}) => {
|
||||||
if (url2 === null || !url2.startsWith(baseUrl)) { return false; }
|
if (url2 === null || !url2.startsWith(baseUrl)) { return false; }
|
||||||
const parsedUrl = new URL(url2);
|
const parsedUrl = new URL(url2);
|
||||||
const baseUrl2 = `${parsedUrl.origin}${parsedUrl.pathname}`;
|
const baseUrl2 = `${parsedUrl.origin}${parsedUrl.pathname}`;
|
||||||
@ -746,7 +746,7 @@ class Backend {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const openInTab = async () => {
|
const openInTab = async () => {
|
||||||
const tab = await this._findTab(1000, isTabMatch);
|
const tab = await this._findTabs(1000, false, predicate, false);
|
||||||
if (tab !== null) {
|
if (tab !== null) {
|
||||||
await this._focusTab(tab);
|
await this._focusTab(tab);
|
||||||
if (queryParams.query) {
|
if (queryParams.query) {
|
||||||
@ -821,16 +821,25 @@ class Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _getOrCreateSearchPopup2() {
|
async _getOrCreateSearchPopup2() {
|
||||||
// Reuse same tab
|
// Use existing tab
|
||||||
const baseUrl = chrome.runtime.getURL('/search.html');
|
const baseUrl = chrome.runtime.getURL('/search.html');
|
||||||
|
const urlPredicate = (url) => url !== null && url.startsWith(baseUrl);
|
||||||
if (this._searchPopupTabId !== null) {
|
if (this._searchPopupTabId !== null) {
|
||||||
const tab = await this._checkTabUrl(this._searchPopupTabId, (url) => url.startsWith(baseUrl));
|
const tab = await this._checkTabUrl(this._searchPopupTabId, urlPredicate);
|
||||||
if (tab !== null) {
|
if (tab !== null) {
|
||||||
return {tab, created: false};
|
return {tab, created: false};
|
||||||
}
|
}
|
||||||
this._searchPopupTabId = null;
|
this._searchPopupTabId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find existing tab
|
||||||
|
const existingTabInfo = await this._findSearchPopupTab(urlPredicate);
|
||||||
|
if (existingTabInfo !== null) {
|
||||||
|
const existingTab = existingTabInfo.tab;
|
||||||
|
this._searchPopupTabId = existingTab.id;
|
||||||
|
return {tab: existingTab, created: false};
|
||||||
|
}
|
||||||
|
|
||||||
// chrome.windows not supported (e.g. on Firefox mobile)
|
// chrome.windows not supported (e.g. on Firefox mobile)
|
||||||
if (!isObject(chrome.windows)) {
|
if (!isObject(chrome.windows)) {
|
||||||
throw new Error('Window creation not supported');
|
throw new Error('Window creation not supported');
|
||||||
@ -863,6 +872,23 @@ class Backend {
|
|||||||
return {tab, created: true};
|
return {tab, created: true};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _findSearchPopupTab(urlPredicate) {
|
||||||
|
const predicate = async ({url, tab}) => {
|
||||||
|
if (!urlPredicate(url)) { return false; }
|
||||||
|
try {
|
||||||
|
const mode = await this._sendMessageTabPromise(
|
||||||
|
tab.id,
|
||||||
|
{action: 'getMode', params: {}},
|
||||||
|
{frameId: 0}
|
||||||
|
);
|
||||||
|
return mode === 'popup';
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return await this._findTabs(1000, false, predicate, true);
|
||||||
|
}
|
||||||
|
|
||||||
_getSearchPopupWindowCreateData(url, options) {
|
_getSearchPopupWindowCreateData(url, options) {
|
||||||
const {popupWindow: {width, height, left, top, useLeft, useTop, windowType}} = options;
|
const {popupWindow: {width, height, left, top, useLeft, useTop, windowType}} = options;
|
||||||
return {
|
return {
|
||||||
@ -1333,33 +1359,74 @@ class Backend {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _findTab(timeout, checkUrl) {
|
_getAllTabs() {
|
||||||
// This function works around the need to have the "tabs" permission to access tab.url.
|
return new Promise((resolve, reject) => {
|
||||||
const tabs = await new Promise((resolve) => chrome.tabs.query({}, resolve));
|
chrome.tabs.query({}, (tabs) => {
|
||||||
const {promise: matchPromise, resolve: matchPromiseResolve} = deferPromise();
|
const e = chrome.runtime.lastError;
|
||||||
|
if (e) {
|
||||||
|
reject(new Error(e.message));
|
||||||
|
} else {
|
||||||
|
resolve(tabs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const checkTabUrl = ({tab, url}) => {
|
async _findTabs(timeout, multiple, predicate, predicateIsAsync) {
|
||||||
if (checkUrl(url, tab)) {
|
// This function works around the need to have the "tabs" permission to access tab.url.
|
||||||
matchPromiseResolve(tab);
|
const tabs = await this._getAllTabs();
|
||||||
|
|
||||||
|
let done = false;
|
||||||
|
const checkTab = async (tab, add) => {
|
||||||
|
const url = await this._getTabUrl(tab.id);
|
||||||
|
|
||||||
|
if (done) { return; }
|
||||||
|
|
||||||
|
let okay = false;
|
||||||
|
const item = {tab, url};
|
||||||
|
try {
|
||||||
|
okay = predicate(item);
|
||||||
|
if (predicateIsAsync) { okay = await okay; }
|
||||||
|
} catch (e) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
|
||||||
|
if (okay && !done) {
|
||||||
|
if (add(item)) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const promises = [];
|
if (multiple) {
|
||||||
for (const tab of tabs) {
|
const results = [];
|
||||||
const promise = this._getTabUrl(tab.id);
|
const add = (value) => {
|
||||||
promise.then((url) => checkTabUrl({url, tab}));
|
results.push(value);
|
||||||
promises.push(promise);
|
return false;
|
||||||
|
};
|
||||||
|
const checkTabPromises = tabs.map((tab) => checkTab(tab, add));
|
||||||
|
await Promise.race([
|
||||||
|
Promise.all(checkTabPromises),
|
||||||
|
promiseTimeout(timeout)
|
||||||
|
]);
|
||||||
|
return results;
|
||||||
|
} else {
|
||||||
|
const {promise, resolve} = deferPromise();
|
||||||
|
let result = null;
|
||||||
|
const add = (value) => {
|
||||||
|
result = value;
|
||||||
|
resolve();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
const checkTabPromises = tabs.map((tab) => checkTab(tab, add));
|
||||||
|
await Promise.race([
|
||||||
|
promise,
|
||||||
|
Promise.all(checkTabPromises),
|
||||||
|
promiseTimeout(timeout)
|
||||||
|
]);
|
||||||
|
resolve();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const racePromises = [
|
|
||||||
matchPromise,
|
|
||||||
Promise.all(promises).then(() => null)
|
|
||||||
];
|
|
||||||
if (typeof timeout === 'number') {
|
|
||||||
racePromises.push(new Promise((resolve) => setTimeout(() => resolve(null), timeout)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Promise.race(racePromises);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _focusTab(tab) {
|
async _focusTab(tab) {
|
||||||
@ -1835,7 +1902,8 @@ class Backend {
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'existingOrNewTab':
|
case 'existingOrNewTab':
|
||||||
if (useSettingsV2) {
|
if (useSettingsV2) {
|
||||||
const tab = await this._findTab(1000, (url2) => (url2 !== null && url2.startsWith(url)));
|
const predicate = ({url: url2}) => (url2 !== null && url2.startsWith(url));
|
||||||
|
const tab = await this._findTabs(1000, false, predicate, false);
|
||||||
if (tab !== null) {
|
if (tab !== null) {
|
||||||
await this._focusTab(tab);
|
await this._focusTab(tab);
|
||||||
} else {
|
} else {
|
||||||
|
@ -139,7 +139,8 @@ class Display extends EventDispatcher {
|
|||||||
['previousEntryDifferentDictionary', () => { this._focusEntryWithDifferentDictionary(-1, true); }]
|
['previousEntryDifferentDictionary', () => { this._focusEntryWithDifferentDictionary(-1, true); }]
|
||||||
]);
|
]);
|
||||||
this.registerMessageHandlers([
|
this.registerMessageHandlers([
|
||||||
['setMode', {async: false, handler: this._onMessageSetMode.bind(this)}]
|
['setMode', {async: false, handler: this._onMessageSetMode.bind(this)}],
|
||||||
|
['getMode', {async: false, handler: this._onMessageGetMode.bind(this)}]
|
||||||
]);
|
]);
|
||||||
this.registerDirectMessageHandlers([
|
this.registerDirectMessageHandlers([
|
||||||
['setOptionsContext', {async: false, handler: this._onMessageSetOptionsContext.bind(this)}],
|
['setOptionsContext', {async: false, handler: this._onMessageSetOptionsContext.bind(this)}],
|
||||||
@ -493,6 +494,10 @@ class Display extends EventDispatcher {
|
|||||||
this._setMode(mode, true);
|
this._setMode(mode, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onMessageGetMode() {
|
||||||
|
return this._mode;
|
||||||
|
}
|
||||||
|
|
||||||
_onMessageSetOptionsContext({optionsContext}) {
|
_onMessageSetOptionsContext({optionsContext}) {
|
||||||
this.setOptionsContext(optionsContext);
|
this.setOptionsContext(optionsContext);
|
||||||
this.searchLast();
|
this.searchLast();
|
||||||
|
Loading…
Reference in New Issue
Block a user