Skip to content

Commit e69be7f

Browse files
committed
Add command to change user-agent
1 parent 075c129 commit e69be7f

File tree

7 files changed

+131
-7
lines changed

7 files changed

+131
-7
lines changed

config/config

+3
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ search-engine wikipedia http://en.wikipedia.org/w/index.php?search={}
1010
set hint-chars = hjklasdfgyuiopqwertnmzxcvb
1111

1212
set cookie-accept = always
13+
14+
# User agents.
15+
add-user-agent firefox Mozilla/5.0 (X11; Linux x86_64; rv:63.0) Gecko/20100101 Firefox/63.0

src/app/mod.rs

+32-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ mod search_engine;
4242
mod server;
4343
mod test_utils;
4444
mod url;
45+
pub mod user_agent;
4546

4647
use std::cell::Cell;
4748
use std::collections::HashMap;
@@ -105,7 +106,12 @@ use titanium_common::Percentage::{self, All, Percent};
105106
use bookmarks::BookmarkManager;
106107
use commands::AppCommand;
107108
use commands::AppCommand::*;
108-
use completers::{BookmarkCompleter, FileCompleter, TagCompleter};
109+
use completers::{
110+
BookmarkCompleter,
111+
FileCompleter,
112+
TagCompleter,
113+
UserAgentCompleter,
114+
};
109115
use config_dir::ConfigDir;
110116
use download_list_view::DownloadListView;
111117
use download_list_view::Msg::{
@@ -120,11 +126,13 @@ use self::config::default_config;
120126
use self::dialog::handle_script_dialog;
121127
use self::file_chooser::handle_file_chooser;
122128
use self::Msg::*;
129+
use self::user_agent::UserAgentManager;
123130
use settings::AppSettings;
124131
use settings::AppSettingsVariant::{
125132
self,
126133
HintChars,
127134
HomePage,
135+
WebkitUserAgent,
128136
};
129137
use urls::canonicalize_url;
130138
use webview::WebView;
@@ -159,6 +167,7 @@ const INIT_SCROLL_TEXT: &str = "[top]";
159167
const RED: RGBA = RGBA { red: 1.0, green: 0.3, blue: 0.2, alpha: 1.0 };
160168
const YELLOW: RGBA = RGBA { red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0 };
161169
const TAG_COMPLETER: &str = "__tag";
170+
pub const USER_AGENT_COMPLETER: &str = "select-user-agent";
162171

163172
static MODES: Modes = &[
164173
Mode { name: "follow", prefix: "f", show_count: false },
@@ -186,6 +195,8 @@ pub struct Model {
186195
scroll_text: String,
187196
search_engines: HashMap<String, String>,
188197
title: String,
198+
user_agents: HashMap<String, String>,
199+
user_agent_manager: UserAgentManager,
189200
web_context: WebContext,
190201
}
191202

@@ -300,6 +311,8 @@ impl Widget for App {
300311
scroll_text: INIT_SCROLL_TEXT.to_string(),
301312
search_engines: HashMap::new(),
302313
title: APP_NAME.to_string(),
314+
user_agents: HashMap::new(),
315+
user_agent_manager: UserAgentManager,
303316
web_context,
304317
}
305318
}
@@ -408,6 +421,7 @@ impl Widget for App {
408421
"win-open" => Box::new(BookmarkCompleter::new("win-open")),
409422
"private-win-open" => Box::new(BookmarkCompleter::new("private-win-open")),
410423
TAG_COMPLETER => Box::new(TagCompleter::new()),
424+
USER_AGENT_COMPLETER => Box::new(UserAgentCompleter::new()),
411425
},
412426
DarkTheme: true,
413427
Title: self.model.title.clone(),
@@ -462,6 +476,14 @@ impl App {
462476
self.mg.emit(Info(format!("Added mark {}", mark as char)));
463477
}
464478

479+
fn add_user_agent(&mut self, user_agent: &str) {
480+
let mut params = user_agent.splitn(2, ' ');
481+
if let (Some(name), Some(user_agent)) = (params.next(), params.next()) {
482+
self.model.user_agents.insert(name.to_string(), user_agent.to_string());
483+
self.model.user_agent_manager.add(name);
484+
}
485+
}
486+
465487
fn adjust_in_follow_mode(&mut self, mode: &str) {
466488
self.model.in_follow_mode.set(mode == "follow");
467489
}
@@ -555,6 +577,7 @@ impl App {
555577
match *command {
556578
ActivateSelection => self.activate_selection(),
557579
AdblockUpdate => handle_error!(self.adblock_update()),
580+
AddUserAgent(ref user_agent) => self.add_user_agent(user_agent),
558581
Back => self.history_back(),
559582
BackwardSearch(ref input) => {
560583
self.webview.emit(SearchBackward(true));
@@ -617,6 +640,7 @@ impl App {
617640
SearchEngine(ref args) => self.add_search_engine(args),
618641
SearchNext => self.webview.emit(PageSearchNext),
619642
SearchPrevious => self.webview.emit(PageSearchPrevious),
643+
SelectUserAgent(ref name) => self.select_user_agent(name),
620644
Stop => self.webview.widget().stop_loading(),
621645
UrlIncrement => self.url_increment(),
622646
UrlDecrement => self.url_decrement(),
@@ -762,6 +786,13 @@ impl App {
762786
}
763787
}
764788

789+
fn select_user_agent(&mut self, name: &str) {
790+
if let Some(user_agent) = self.model.user_agents.get(name).cloned() {
791+
self.info(format!("Set user agent to: {}", user_agent));
792+
self.setting_changed(WebkitUserAgent(user_agent));
793+
}
794+
}
795+
765796
fn set_mode(&mut self, mode: &'static str) {
766797
self.adjust_in_follow_mode(mode);
767798
self.mg.emit(SetMode(mode));

src/app/user_agent.rs

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2018 Boucher, Antoni <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of
5+
* this software and associated documentation files (the "Software"), to deal in
6+
* the Software without restriction, including without limitation the rights to
7+
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8+
* the Software, and to permit persons to whom the Software is furnished to do so,
9+
* subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16+
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18+
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*/
21+
22+
use std::cell::Cell;
23+
use std::rc::Rc;
24+
25+
thread_local! {
26+
static MANAGER: Rc<Cell<Vec<String>>> = Rc::new(Cell::new(vec![]));
27+
}
28+
29+
pub struct UserAgentManager;
30+
31+
impl UserAgentManager {
32+
pub fn add(&self, name: &str) {
33+
MANAGER.with(|key| {
34+
let mut names = key.take();
35+
names.push(name.to_string());
36+
key.set(names);
37+
})
38+
}
39+
40+
pub fn get_all(&self) -> Vec<String> {
41+
MANAGER.with(|key| {
42+
let names = key.take();
43+
let result = names.clone();
44+
key.set(names);
45+
result
46+
})
47+
}
48+
}

src/commands.rs

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub enum AppCommand {
2525
ActivateSelection,
2626
#[help(text="Update the host file used by the adblocker")]
2727
AdblockUpdate,
28+
#[help(text="Add a new user agent")]
29+
AddUserAgent(String),
2830
#[help(text="Go back in the history")]
2931
Back,
3032
#[special_command(incremental, identifier="?")]
@@ -139,6 +141,8 @@ pub enum AppCommand {
139141
SearchNext,
140142
#[completion(hidden)]
141143
SearchPrevious,
144+
#[help(text="Select a user agent by name")]
145+
SelectUserAgent(String),
142146
#[help(text="Stop loading the current page")]
143147
Stop,
144148
#[completion(hidden)]

src/completers.rs

+36
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ use std::path::{Path, PathBuf};
2626
use mg::completion::{Completer, CompletionCell, CompletionResult};
2727
use mg::completion::Column::{self, AllVisible, Expand};
2828

29+
use app::USER_AGENT_COMPLETER;
30+
use app::user_agent::UserAgentManager;
2931
use bookmarks::{BookmarkInput, BookmarkManager};
3032
use download::download_dir;
3133

@@ -238,6 +240,40 @@ impl Completer for TagCompleter {
238240
}
239241
}
240242

243+
/// A user agent completer.
244+
pub struct UserAgentCompleter {
245+
manager: UserAgentManager,
246+
}
247+
248+
impl UserAgentCompleter {
249+
pub fn new() -> Self {
250+
Self {
251+
manager: UserAgentManager,
252+
}
253+
}
254+
}
255+
256+
impl Completer for UserAgentCompleter {
257+
fn columns(&self) -> Vec<Column> {
258+
vec![AllVisible]
259+
}
260+
261+
fn complete_result(&self, value: &str) -> String {
262+
format!("{} {}", USER_AGENT_COMPLETER, value)
263+
}
264+
265+
fn completions(&mut self, input: &str) -> Vec<CompletionResult> {
266+
let mut results = vec![];
267+
let names = self.manager.get_all();
268+
for name in names {
269+
if name.contains(input) {
270+
results.push(CompletionResult::new(&[&name]));
271+
}
272+
}
273+
results
274+
}
275+
}
276+
241277
/// Split at whitespaces and at the # character.
242278
/// The # character will be kept in the words while the spaces are dropped.
243279
fn split_whitespace_and_hash(input: &str) -> Vec<String> {

src/main.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,6 @@
7979
* TODO: shortcut to insert the password in insert mode?
8080
* TODO: shortcut to open in the same window.
8181
*
82-
* FIXME: web extension panics with newer webkit2gtk-webextension crate.
83-
*
8482
* FIXME: slow startup (The update function was slow to execute for message NewApp: 1943ms).
8583
* Perhaps should init table and cleanup download folder in another thread.
8684
*
@@ -92,7 +90,6 @@
9290
*
9391
* FIXME: config file marks not created.
9492
*
95-
* TODO: set user agent.
9693
* TODO: may need to enable fullscreen API (in config) to allow real fullscreen.
9794
* Most probably needs to hide the status bar in the signal enter_fullscreen.
9895
*
@@ -139,6 +136,7 @@
139136
*
140137
* TODO: Command to know which pages are in which process:
141138
* * to check whether the pages are distributed evenly between the processes.
139+
* * maybe to know if a process is stuck.
142140
*
143141
* TODO: hide HTML in title/bookmarks?
144142
*
@@ -179,13 +177,11 @@
179177
* TODO: webkit_web_view_get_main_resource() to get source code
180178
* TODO: Show hints on elements with an ID (to be able to navigate to their anchor).
181179
*
182-
* FIXME: angular form needs the typing action to be done in order to submit: https://grafana.int.adgear.com/
180+
* FIXME: angular form needs the typing action to be done in order to submit: https://www.codingame.com/start
183181
*
184182
* FIXME: seems slower when running as normal user (and faster as root), so perhaps the config slow
185183
* it down. Looks like it is slowed down by the hard drive.
186184
*
187-
* TODO: switch to relm-epoll, a crate that polls epoll from glib.
188-
*
189185
* TODO: plugin to hide disqus.
190186
*
191187
* TODO: downloading a non-existing file (http://download.microsoft.com/download/8/8/8/888f34b7-4f54-4f06-8dac-fa29b19f33dd/msxml3.msi) causes an error.

titanium-web-extension/src/login_form.rs

+6
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ fn get_login_form(document: &DOMDocument) -> Option<DOMHTMLFormElement> {
123123

124124
/// Load the password in the login form.
125125
pub fn load_password(document: &DOMDocument, password: &str) {
126+
// TODO: trigger change event (https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js/46012210#46012210):
127+
// var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
128+
// nativeInputValueSetter.call(element, 'react 16 value');
129+
// var ev2 = new Event('input', { bubbles: true});
130+
// element.dispatchEvent(ev2);
126131
let password_inputs =
127132
find_login_form(document)
128133
.and_then(|login_form| login_form.query_selector_all("input[type='password']").ok());
@@ -141,6 +146,7 @@ pub fn load_password(document: &DOMDocument, password: &str) {
141146

142147
/// Load the username in the login form.
143148
pub fn load_username(document: &DOMDocument, username: &str) {
149+
// TODO: trigger change event:
144150
let username_inputs =
145151
find_login_form(document)
146152
.and_then(|login_form|

0 commit comments

Comments
 (0)