Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Safer query selector #364

Merged
merged 10 commits into from
Dec 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions ext/js/display/display-anki.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {EventListenerCollection, deferPromise} from '../core.js';
import {AnkiNoteBuilder} from '../data/anki-note-builder.js';
import {AnkiUtil} from '../data/anki-util.js';
import {PopupMenu} from '../dom/popup-menu.js';
import {querySelectorNotNull} from '../dom/query-selector.js';
import {yomitan} from '../yomitan.js';

export class DisplayAnki {
Expand Down Expand Up @@ -91,7 +92,7 @@ export class DisplayAnki {
['term', ['term-kanji', 'term-kana']]
]);
/** @type {HTMLElement} */
this._menuContainer = /** @type {HTMLElement} */ (document.querySelector('#popup-menus'));
this._menuContainer = querySelectorNotNull(document, '#popup-menus');
/** @type {(event: MouseEvent) => void} */
this._onShowTagsBind = this._onShowTags.bind(this);
/** @type {(event: MouseEvent) => void} */
Expand Down Expand Up @@ -827,7 +828,8 @@ export class DisplayAnki {
button.hidden = disabled;
button.dataset.noteIds = allNoteIds.join(' ');

const badge = /** @type {?HTMLElement} */ (button.querySelector('.action-button-badge'));
/** @type {?HTMLElement} */
const badge = button.querySelector('.action-button-badge');
if (badge !== null) {
const badgeData = badge.dataset;
if (allNoteIds.length > 1) {
Expand Down Expand Up @@ -866,13 +868,17 @@ export class DisplayAnki {
const noteIds = this._getNodeNoteIds(node);
if (noteIds.length === 0) { return; }

const menuContainerNode = /** @type {HTMLElement} */ (this._display.displayGenerator.instantiateTemplate('view-note-button-popup-menu'));
const menuBodyNode = /** @type {HTMLElement} */ (menuContainerNode.querySelector('.popup-menu-body'));
/** @type {HTMLElement} */
const menuContainerNode = this._display.displayGenerator.instantiateTemplate('view-note-button-popup-menu');
/** @type {HTMLElement} */
const menuBodyNode = querySelectorNotNull(menuContainerNode, '.popup-menu-body');

for (let i = 0, ii = noteIds.length; i < ii; ++i) {
const noteId = noteIds[i];
const item = /** @type {HTMLElement} */ (this._display.displayGenerator.instantiateTemplate('view-note-button-popup-menu-item'));
const label = /** @type {Element} */ (item.querySelector('.popup-menu-item-label'));
/** @type {HTMLElement} */
const item = this._display.displayGenerator.instantiateTemplate('view-note-button-popup-menu-item');
/** @type {Element} */
const label = querySelectorNotNull(item, '.popup-menu-item-label');
label.textContent = `Note ${i + 1}: ${noteId}`;
item.dataset.menuAction = 'viewNote';
item.dataset.noteIds = `${noteId}`;
Expand Down
18 changes: 12 additions & 6 deletions ext/js/display/display-audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import {EventListenerCollection} from '../core.js';
import {PopupMenu} from '../dom/popup-menu.js';
import {querySelectorNotNull} from '../dom/query-selector.js';
import {AudioSystem} from '../media/audio-system.js';
import {yomitan} from '../yomitan.js';

Expand Down Expand Up @@ -45,7 +46,7 @@ export class DisplayAudio {
/** @type {Map<string, import('display-audio').CacheItem>} */
this._cache = new Map();
/** @type {Element} */
this._menuContainer = /** @type {Element} */ (document.querySelector('#popup-menus'));
this._menuContainer = querySelectorNotNull(document, '#popup-menus');
/** @type {import('core').TokenObject} */
this._entriesToken = {};
/** @type {Set<PopupMenu>} */
Expand Down Expand Up @@ -715,7 +716,8 @@ export class DisplayAudio {
button.dataset.potentialAvailableAudioCount = `${potentialAvailableAudioCount}`;
}

const badge = /** @type {?HTMLElement} */ (button.querySelector('.action-button-badge'));
/** @type {?HTMLElement} */
const badge = button.querySelector('.action-button-badge');
if (badge === null) { return; }

const badgeData = badge.dataset;
Expand Down Expand Up @@ -804,7 +806,8 @@ export class DisplayAudio {
_createMenu(sourceButton, term, reading) {
// Create menu
const menuContainerNode = /** @type {HTMLElement} */ (this._display.displayGenerator.instantiateTemplate('audio-button-popup-menu'));
const menuBodyNode = /** @type {HTMLElement} */ (menuContainerNode.querySelector('.popup-menu-body'));
/** @type {HTMLElement} */
const menuBodyNode = querySelectorNotNull(menuContainerNode, '.popup-menu-body');
menuContainerNode.dataset.term = term;
menuContainerNode.dataset.reading = reading;

Expand Down Expand Up @@ -837,7 +840,8 @@ export class DisplayAudio {
const existingNode = this._getOrCreateMenuItem(currentItems, index, subIndex);
const node = existingNode !== null ? existingNode : /** @type {HTMLElement} */ (displayGenerator.instantiateTemplate('audio-button-popup-menu-item'));

const labelNode = /** @type {HTMLElement} */ (node.querySelector('.popup-menu-item-audio-button .popup-menu-item-label'));
/** @type {HTMLElement} */
const labelNode = querySelectorNotNull(node, '.popup-menu-item-audio-button .popup-menu-item-label');
let label = name;
if (!nameUnique) {
label = `${label} ${nameIndex + 1}`;
Expand All @@ -847,11 +851,13 @@ export class DisplayAudio {
if (typeof subName === 'string' && subName.length > 0) { label += `: ${subName}`; }
labelNode.textContent = label;

const cardButton = /** @type {HTMLElement} */ (node.querySelector('.popup-menu-item-set-primary-audio-button'));
/** @type {HTMLElement} */
const cardButton = querySelectorNotNull(node, '.popup-menu-item-set-primary-audio-button');
cardButton.hidden = !downloadable;

if (valid !== null) {
const icon = /** @type {HTMLElement} */ (node.querySelector('.popup-menu-item-audio-button .popup-menu-item-icon'));
/** @type {HTMLElement} */
const icon = querySelectorNotNull(node, '.popup-menu-item-audio-button .popup-menu-item-icon');
icon.dataset.icon = valid ? 'checkmark' : 'cross';
showIcons = true;
}
Expand Down
5 changes: 3 additions & 2 deletions ext/js/display/display-notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

import {EventListenerCollection} from '../core.js';
import {querySelectorNotNull} from '../dom/query-selector.js';

export class DisplayNotification {
/**
Expand All @@ -29,9 +30,9 @@ export class DisplayNotification {
/** @type {HTMLElement} */
this._node = node;
/** @type {HTMLElement} */
this._body = /** @type {HTMLElement} */ (node.querySelector('.footer-notification-body'));
this._body = querySelectorNotNull(node, '.footer-notification-body');
/** @type {HTMLElement} */
this._closeButton = /** @type {HTMLElement} */ (node.querySelector('.footer-notification-close-button'));
this._closeButton = querySelectorNotNull(node, '.footer-notification-close-button');
/** @type {EventListenerCollection} */
this._eventListeners = new EventListenerCollection();
/** @type {?import('core').Timeout} */
Expand Down
15 changes: 10 additions & 5 deletions ext/js/display/display-profile-selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import {EventListenerCollection, generateId} from '../core.js';
import {PanelElement} from '../dom/panel-element.js';
import {querySelectorNotNull} from '../dom/query-selector.js';
import {yomitan} from '../yomitan.js';

export class DisplayProfileSelection {
Expand All @@ -28,12 +29,14 @@ export class DisplayProfileSelection {
/** @type {import('./display.js').Display} */
this._display = display;
/** @type {HTMLElement} */
this._profielList = /** @type {HTMLElement} */ (document.querySelector('#profile-list'));
this._profielList = querySelectorNotNull(document, '#profile-list');
/** @type {HTMLButtonElement} */
this._profileButton = /** @type {HTMLButtonElement} */ (document.querySelector('#profile-button'));
this._profileButton = querySelectorNotNull(document, '#profile-button');
/** @type {HTMLElement} */
const profilePanelElement = querySelectorNotNull(document, '#profile-panel');
/** @type {PanelElement} */
this._profilePanel = new PanelElement({
node: /** @type {HTMLElement} */ (document.querySelector('#profile-panel')),
node: profilePanelElement,
closingAnimationDuration: 375 // Milliseconds; includes buffer
});
/** @type {boolean} */
Expand Down Expand Up @@ -98,9 +101,11 @@ export class DisplayProfileSelection {
for (let i = 0, ii = profiles.length; i < ii; ++i) {
const {name} = profiles[i];
const entry = displayGenerator.createProfileListItem();
const radio = /** @type {HTMLInputElement} */ (entry.querySelector('.profile-entry-is-default-radio'));
/** @type {HTMLInputElement} */
const radio = querySelectorNotNull(entry, '.profile-entry-is-default-radio');
radio.checked = (i === profileCurrent);
const nameNode = /** @type {Element} */ (entry.querySelector('.profile-list-item-name'));
/** @type {Element} */
const nameNode = querySelectorNotNull(entry, '.profile-list-item-name');
nameNode.textContent = name;
fragment.appendChild(entry);
this._eventListeners.addEventListener(radio, 'change', this._onProfileRadioChange.bind(this, i), false);
Expand Down
40 changes: 23 additions & 17 deletions ext/js/display/display.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {ThemeController} from '../app/theme-controller.js';
import {FrameEndpoint} from '../comm/frame-endpoint.js';
import {DynamicProperty, EventDispatcher, EventListenerCollection, clone, deepEqual, invokeMessageHandler, log, promiseTimeout} from '../core.js';
import {PopupMenu} from '../dom/popup-menu.js';
import {querySelectorNotNull} from '../dom/query-selector.js';
import {ScrollElement} from '../dom/scroll-element.js';
import {HotkeyHelpController} from '../input/hotkey-help-controller.js';
import {TextScanner} from '../language/text-scanner.js';
Expand Down Expand Up @@ -62,7 +63,7 @@ export class Display extends EventDispatcher {
/** @type {import('../input/hotkey-handler.js').HotkeyHandler} */
this._hotkeyHandler = hotkeyHandler;
/** @type {HTMLElement} */
this._container = /** @type {HTMLElement} */ (document.querySelector('#dictionary-entries'));
this._container = querySelectorNotNull(document, '#dictionary-entries');
/** @type {import('dictionary').DictionaryEntry[]} */
this._dictionaryEntries = [];
/** @type {HTMLElement[]} */
Expand Down Expand Up @@ -116,7 +117,7 @@ export class Display extends EventDispatcher {
/** @type {number} */
this._queryOffset = 0;
/** @type {HTMLElement} */
this._progressIndicator = /** @type {HTMLElement} */ (document.querySelector('#progress-indicator'));
this._progressIndicator = querySelectorNotNull(document, '#progress-indicator');
/** @type {?import('core').Timeout} */
this._progressIndicatorTimer = null;
/** @type {DynamicProperty<boolean>} */
Expand All @@ -126,24 +127,24 @@ export class Display extends EventDispatcher {
/** @type {?boolean} */
this._queryParserVisibleOverride = null;
/** @type {HTMLElement} */
this._queryParserContainer = /** @type {HTMLElement} */ (document.querySelector('#query-parser-container'));
this._queryParserContainer = querySelectorNotNull(document, '#query-parser-container');
/** @type {QueryParser} */
this._queryParser = new QueryParser({
getSearchContext: this._getSearchContext.bind(this),
japaneseUtil
});
/** @type {HTMLElement} */
this._contentScrollElement = /** @type {HTMLElement} */ (document.querySelector('#content-scroll'));
this._contentScrollElement = querySelectorNotNull(document, '#content-scroll');
/** @type {HTMLElement} */
this._contentScrollBodyElement = /** @type {HTMLElement} */ (document.querySelector('#content-body'));
this._contentScrollBodyElement = querySelectorNotNull(document, '#content-body');
/** @type {ScrollElement} */
this._windowScroll = new ScrollElement(this._contentScrollElement);
/** @type {HTMLButtonElement} */
this._closeButton = /** @type {HTMLButtonElement} */ (document.querySelector('#close-button'));
/** @type {HTMLButtonElement} */
this._navigationPreviousButton = /** @type {HTMLButtonElement} */ (document.querySelector('#navigate-previous-button'));
/** @type {HTMLButtonElement} */
this._navigationNextButton = /** @type {HTMLButtonElement} */ (document.querySelector('#navigate-next-button'));
/** @type {?HTMLButtonElement} */
this._closeButton = document.querySelector('#close-button');
/** @type {?HTMLButtonElement} */
this._navigationPreviousButton = document.querySelector('#navigate-previous-button');
/** @type {?HTMLButtonElement} */
this._navigationNextButton = document.querySelector('#navigate-next-button');
/** @type {?Frontend} */
this._frontend = null;
/** @type {?Promise<void>} */
Expand Down Expand Up @@ -171,15 +172,15 @@ export class Display extends EventDispatcher {
/** @type {?import('./display-notification.js').DisplayNotification} */
this._tagNotification = null;
/** @type {HTMLElement} */
this._footerNotificationContainer = /** @type {HTMLElement} */ (document.querySelector('#content-footer'));
this._footerNotificationContainer = querySelectorNotNull(document, '#content-footer');
/** @type {OptionToggleHotkeyHandler} */
this._optionToggleHotkeyHandler = new OptionToggleHotkeyHandler(this);
/** @type {ElementOverflowController} */
this._elementOverflowController = new ElementOverflowController();
/** @type {boolean} */
this._frameVisible = (pageType === 'search');
/** @type {HTMLElement} */
this._menuContainer = /** @type {HTMLElement} */ (document.querySelector('#popup-menus'));
this._menuContainer = querySelectorNotNull(document, '#popup-menus');
/** @type {(event: MouseEvent) => void} */
this._onEntryClickBind = this._onEntryClick.bind(this);
/** @type {(event: MouseEvent) => void} */
Expand Down Expand Up @@ -1041,15 +1042,18 @@ export class Display extends EventDispatcher {
const node = /** @type {HTMLElement} */ (e.currentTarget);

const menuContainerNode = /** @type {HTMLElement} */ (this._displayGenerator.instantiateTemplate('dictionary-entry-popup-menu'));
const menuBodyNode = /** @type {HTMLElement} */ (menuContainerNode.querySelector('.popup-menu-body'));
/** @type {HTMLElement} */
const menuBodyNode = querySelectorNotNull(menuContainerNode, '.popup-menu-body');

/**
* @param {string} menuAction
* @param {string} label
*/
const addItem = (menuAction, label) => {
const item = /** @type {HTMLElement} */ (this._displayGenerator.instantiateTemplate('dictionary-entry-popup-menu-item'));
/** @type {HTMLElement} */ (item.querySelector('.popup-menu-item-label')).textContent = label;
/** @type {HTMLElement} */
const labelElement = querySelectorNotNull(item, '.popup-menu-item-label');
labelElement.textContent = label;
item.dataset.menuAction = menuAction;
menuBodyNode.appendChild(item);
};
Expand Down Expand Up @@ -1291,7 +1295,8 @@ export class Display extends EventDispatcher {

/** */
_setContentExtensionUnloaded() {
const errorExtensionUnloaded = /** @type {?HTMLElement} */ (document.querySelector('#error-extension-unloaded'));
/** @type {?HTMLElement} */
const errorExtensionUnloaded = document.querySelector('#error-extension-unloaded');

if (this._container !== null) {
this._container.hidden = true;
Expand Down Expand Up @@ -1323,7 +1328,8 @@ export class Display extends EventDispatcher {
* @param {boolean} visible
*/
_setNoContentVisible(visible) {
const noResults = /** @type {?HTMLElement} */ (document.querySelector('#no-results'));
/** @type {?HTMLElement} */
const noResults = document.querySelector('#no-results');

if (noResults !== null) {
noResults.hidden = !visible;
Expand Down
7 changes: 4 additions & 3 deletions ext/js/display/query-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

import {EventDispatcher, log} from '../core.js';
import {querySelectorNotNull} from '../dom/query-selector.js';
import {TextScanner} from '../language/text-scanner.js';
import {yomitan} from '../yomitan.js';

Expand Down Expand Up @@ -50,11 +51,11 @@ export class QueryParser extends EventDispatcher {
/** @type {import('api').ParseTextResult} */
this._parseResults = [];
/** @type {HTMLElement} */
this._queryParser = /** @type {HTMLElement} */ (document.querySelector('#query-parser-content'));
this._queryParser = querySelectorNotNull(document, '#query-parser-content');
/** @type {HTMLElement} */
this._queryParserModeContainer = /** @type {HTMLElement} */ (document.querySelector('#query-parser-mode-container'));
this._queryParserModeContainer = querySelectorNotNull(document, '#query-parser-mode-container');
/** @type {HTMLSelectElement} */
this._queryParserModeSelect = /** @type {HTMLSelectElement} */ (document.querySelector('#query-parser-mode-select'));
this._queryParserModeSelect = querySelectorNotNull(document, '#query-parser-mode-select');
/** @type {TextScanner} */
this._textScanner = new TextScanner({
node: this._queryParser,
Expand Down
13 changes: 7 additions & 6 deletions ext/js/display/search-display-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import * as wanakana from '../../lib/wanakana.js';
import {ClipboardMonitor} from '../comm/clipboard-monitor.js';
import {EventListenerCollection, invokeMessageHandler} from '../core.js';
import {querySelectorNotNull} from '../dom/query-selector.js';
import {yomitan} from '../yomitan.js';

export class SearchDisplayController {
Expand All @@ -42,17 +43,17 @@ export class SearchDisplayController {
/** @type {import('./search-persistent-state-controller.js').SearchPersistentStateController} */
this._searchPersistentStateController = searchPersistentStateController;
/** @type {HTMLButtonElement} */
this._searchButton = /** @type {HTMLButtonElement} */ (document.querySelector('#search-button'));
this._searchButton = querySelectorNotNull(document, '#search-button');
/** @type {HTMLButtonElement} */
this._searchBackButton = /** @type {HTMLButtonElement} */ (document.querySelector('#search-back-button'));
this._searchBackButton = querySelectorNotNull(document, '#search-back-button');
/** @type {HTMLTextAreaElement} */
this._queryInput = /** @type {HTMLTextAreaElement} */ (document.querySelector('#search-textbox'));
this._queryInput = querySelectorNotNull(document, '#search-textbox');
/** @type {HTMLElement} */
this._introElement = /** @type {HTMLElement} */ (document.querySelector('#intro'));
this._introElement = querySelectorNotNull(document, '#intro');
/** @type {HTMLInputElement} */
this._clipboardMonitorEnableCheckbox = /** @type {HTMLInputElement} */ (document.querySelector('#clipboard-monitor-enable'));
this._clipboardMonitorEnableCheckbox = querySelectorNotNull(document, '#clipboard-monitor-enable');
/** @type {HTMLInputElement} */
this._wanakanaEnableCheckbox = /** @type {HTMLInputElement} */ (document.querySelector('#wanakana-enable'));
this._wanakanaEnableCheckbox = querySelectorNotNull(document, '#wanakana-enable');
/** @type {EventListenerCollection} */
this._queryInputEvents = new EventListenerCollection();
/** @type {boolean} */
Expand Down
Loading