browsh/webext/src/background/tty_commands_mixin.js
2019-06-24 09:09:58 +03:00

264 lines
7.3 KiB
JavaScript

import utils from "utils";
// Handle commands coming in from the terminal, like; STDIN keystrokes, TTY resize, etc
export default MixinBase =>
class extends MixinBase {
handleTerminalMessage(message) {
const parts = message.split(",");
const command = parts[0];
switch (command) {
case "/config":
this._loadConfig(message.slice(8));
break;
case "/tab_command":
this.sendToCurrentTab(message.slice(13));
break;
case "/tty_size":
this._updateTTYSize(parts[1], parts[2]);
break;
case "/stdin":
this._handleUICommand(parts);
this.sendToCurrentTab(message);
break;
case "/url_bar":
this._handleURLBarInput(parts.slice(1).join(","));
break;
case "/new_tab":
this.createNewTab(parts.slice(1).join(","));
break;
case "/switch_to_tab":
this.switchToTab(parts.slice(1).join(","));
break;
case "/duplicate_tab":
this.duplicateTab(parts.slice(1).join(","));
break;
case "/remove_tab":
this.removeTab(parts.slice(1).join(","));
break;
case "/restore_tab":
this.restoreTab();
break;
case "/raw_text_request":
this._rawTextRequest(parts[1], parts[2], parts.slice(3).join(","));
break;
}
}
_loadConfig(json_string) {
this.log(json_string);
this.config = JSON.parse(json_string);
this.config.browsh_version = browser.runtime.getManifest().version;
if (this.currentTab()) {
this.currentTab().sendGlobalConfig(this.config);
}
this.dimensions.postConfigSetup(this.config);
this._setupRawTextMode();
}
_setupRawTextMode() {
if (!this.config["http-server-mode"]) {
return;
}
this._is_raw_text_mode = true;
this._updateTTYSize(
this.dimensions.raw_text_tty_size.width,
this.dimensions.raw_text_tty_size.height
);
}
_updateTTYSize(width, height) {
this.dimensions.tty.width = parseInt(width);
this.dimensions.tty.height = parseInt(height);
if (this.currentTab()) {
this.sendToCurrentTab(
`/tty_size,${this.dimensions.tty.width},${this.dimensions.tty.height}`
);
}
this.log(
`Requesting browser resize for new TTY dimensions: ` +
`${width}x${height}`
);
this.dimensions.resizeBrowserWindow();
}
_handleUICommand(parts) {
const input = JSON.parse(utils.rebuildArgsToSingleArg(parts));
// CTRL mappings
/*
if (input.mod === 2) {
switch (input.char) {
default:
}
}
*/
// ALT mappings
if (input.mod === 4) {
switch (input.char) {
case "p":
this.screenshotActiveTab();
break;
case "u":
this.toggleUserAgent();
break;
}
}
return false;
}
_handleURLBarInput(input) {
const final_url = this._getURLfromUserInput(input);
this.gotoURL(final_url);
}
// TODO: move to CLI client
_getURLfromUserInput(input) {
let url;
const search_engine = this.config.default_search_engine_base;
// Basically just check to see if there is text either side of a dot
const is_straddled_dot = RegExp(/^[^\s]+\.[^\s]+/);
// More comprehensive URL pattern
const is_url = RegExp(/\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[.\w]*)*$/);
if (is_straddled_dot.test(input) || is_url.test(input)) {
url = input;
if (!url.startsWith("http")) {
url = "http://" + url;
}
} else {
url = `${search_engine}${input}`;
}
this.urlBarUserContent = url;
return url;
}
createNewTab(url, callback) {
const final_url = this._getURLfromUserInput(url);
let creating = browser.tabs.create({
url: final_url
});
creating.then(
tab => {
if (callback) {
callback(tab);
}
this.log(`New tab created: ${tab}`);
},
error => {
this.log(`Error creating new tab: ${error}`);
}
);
}
gotoURL(url) {
let updating = browser.tabs.update(parseInt(this.currentTab().id), {
url: url
});
updating.then(
tab => {
this.log(`Tab ${tab.id} loaded: ${url}`);
},
error => {
this.log(`Error loading: ${url} \nError: ${error}`);
}
);
}
switchToTab(id) {
let updating = browser.tabs.update(parseInt(id), {
active: true
});
updating.then(
tab => {
this.log(`Switched to tab: ${tab.id}`);
},
error => {
this.log(`Error switching to tab: ${error}`);
}
);
}
removeTab(id) {
this.tabs[id].remove();
this.tabs[id] = null;
}
duplicateTab(id) {
browser.tabs.duplicate(parseInt(id));
}
restoreTab() {
var sessionsInfo = browser.sessions.getRecentlyClosed({ maxResults: 1 });
sessionsInfo.then(this._restoreTab);
}
_restoreTab(sessionsInfo) {
var mySessionInfo = sessionsInfo[0];
if (mySessionInfo.tab) {
browser.sessions.restore(mySessionInfo.tab.sessionId);
} else {
browser.sessions.restore(mySessionInfo.window.sessionId);
}
}
// We use the `browser` object here rather than going into the actual content script
// because the content script may have crashed, even never loaded.
screenshotActiveTab() {
const capturing = browser.tabs.captureVisibleTab({
format: "jpeg"
});
capturing.then(this._saveScreenshot.bind(this), error => this.log(error));
}
_saveScreenshot(imageUri) {
const data = imageUri.replace(/^data:image\/\w+;base64,/, "");
this.sendToTerminal("/screenshot," + data);
}
_rawTextRequest(request_id, mode, url) {
this.createNewTab(url, native_tab => {
this._acknowledgeNewTab({
id: native_tab.id,
request_id: request_id,
raw_text_mode_type: mode.toLowerCase(),
start_time: Date.now()
});
// Sometimes tabs fail to load for whatever reason. Make sure they get
// removed to save RAM in long-lived Browsh HTTP servers
setTimeout(() => {
if (this.tabs[native_tab.id]) {
this.removeTab(native_tab.id);
}
}, 60000);
});
}
toggleUserAgent() {
let message;
this._is_using_mobile_user_agent = !this._is_using_mobile_user_agent;
message = this._is_using_mobile_user_agent
? "Mobile user agent active"
: "Desktop user agent active";
this.currentTab().updateStatus("info", message);
}
_addUserAgentListener() {
browser.webRequest.onBeforeSendHeaders.addListener(
e => {
if (this._is_using_mobile_user_agent) {
e.requestHeaders.forEach(header => {
if (header.name.toLowerCase() == "user-agent") {
header.value = this.config.mobile_user_agent;
}
});
return {
requestHeaders: e.requestHeaders
};
}
},
{
urls: ["*://*/*"]
},
["blocking", "requestHeaders"]
);
}
};