1 When /^I (?:try to )?start the Unsafe Browser(?: through the GNOME menu)?$/ do
2 step 'I start "Unsafe Browser" via GNOME Activities Overview'
5 When /^I successfully start the Unsafe Browser$/ do
6 step 'I start the Unsafe Browser'
7 step 'I see and accept the Unsafe Browser start verification'
8 step 'I see the "Starting the Unsafe Browser..." notification ' \
9 'after at most 60 seconds'
10 step 'the Unsafe Browser has started'
13 # This step works reliably only when there's no more than one tab:
14 # otherwise, browser.tabs.warnOnClose will block this with a
15 # "Quit and close tabs?" dialog.
16 When /^I close the (?:Tor|Unsafe) Browser$/ do
17 @screen.press('ctrl', 'q')
20 When(/^I kill the ((?:Tor|Unsafe) Browser)$/) do |browser|
21 info = xul_application_info(browser)
22 $vm.execute_successfully("pkill --full --exact '#{info[:cmd_regex]}'")
24 $vm.execute("pgrep --full --exact '#{info[:cmd_regex]}'").failure?
28 def tor_browser_application_info(defaults)
30 binary = $vm.execute_successfully(
31 'echo ${TBB_INSTALL}/firefox.real', libs: 'tor-browser'
33 cmd_regex = "#{binary} .* -profile " \
34 "/home/#{user}/\.tor-browser/profile\.default"
40 new_tab_button_image: 'TorBrowserNewTabButton.png',
41 browser_reload_button_image: 'TorBrowserReloadButton.png',
42 browser_stop_button_image: 'TorBrowserStopButton.png',
47 def unsafe_browser_application_info(defaults)
49 binary = $vm.execute_successfully(
50 'echo ${TBB_INSTALL}/firefox.real', libs: 'tor-browser'
52 cmd_regex = "#{binary} .* " \
53 "-profile /home/#{user}/\.unsafe-browser/profile\.default"
58 chroot: '/var/lib/unsafe-browser/chroot',
59 new_tab_button_image: 'UnsafeBrowserNewTabButton.png',
60 browser_reload_button_image: 'UnsafeBrowserReloadButton.png',
61 browser_stop_button_image: 'UnsafeBrowserStopButton.png',
66 def tor_launcher_application_info(defaults)
68 # We do not enable AppArmor confinement for the Tor Launcher.
69 binary = $vm.execute_successfully(
70 'echo ${TBB_INSTALL}/firefox-unconfined', libs: 'tor-browser'
72 tor_launcher_install = $vm.execute_successfully(
73 'echo ${TOR_LAUNCHER_INSTALL}', libs: 'tor-browser'
75 cmd_regex = "#{binary}\s+-app #{tor_launcher_install}/application\.ini.*"
81 new_tab_button_image: nil,
82 browser_reload_button_image: nil,
83 browser_stop_button_image: nil,
84 address_bar_images: [],
85 # The standalone Tor Launcher uses fewer libs than the full
87 unused_tbb_libs: defaults[:unused_tbb_libs]
88 .concat(['libfreebl3.so', 'libfreeblpriv3.so',
89 'libnssckbi.so', 'libsoftokn3.so',]),
94 def xul_application_info(application)
96 address_bar_images: ["BrowserAddressBar#{$language}.png",
97 "BrowserAddressBar#{$language}Alt.png",],
98 unused_tbb_libs: ['libnssdbm3.so', 'libmozavcodec.so', 'libmozavutil.so'],
102 tor_browser_application_info(defaults)
103 when 'Unsafe Browser'
104 unsafe_browser_application_info(defaults)
106 tor_launcher_application_info(defaults)
108 raise "Invalid browser or XUL application: #{application}"
112 When /^I open a new tab in the (.*)$/ do |browser|
113 info = xul_application_info(browser)
114 @screen.click(info[:new_tab_button_image])
115 @screen.wait_any(info[:address_bar_images], 10)
118 When /^I open the address "([^"]*)" in the (.*)$/ do |address, browser|
119 step "I open a new tab in the #{browser}"
120 info = xul_application_info(browser)
121 open_address = proc do
122 @screen.find_any(info[:address_bar_images])[:match].click
123 # This static here since we have no reliable visual indicators
124 # that we can watch to know when typing is "safe".
126 # The browser sometimes loses keypresses when suggestions are
127 # shown, which we work around by pasting the address from the
128 # clipboard, in one go.
129 $vm.set_clipboard(address)
130 @screen.press('ctrl', 'v')
131 @screen.press('Return')
133 recovery_on_failure = proc do
134 @screen.press('Escape')
135 @screen.wait_vanish(info[:browser_stop_button_image], 3)
138 retry_method = if browser == 'Tor Browser'
141 proc { |p, &b| retry_action(10, recovery_proc: p, &b) }
144 retry_method.call(recovery_on_failure) do
145 @screen.wait_vanish(info[:browser_stop_button_image], 120)
146 @screen.wait(info[:browser_reload_button_image], 120)
150 def page_has_loaded_in_the_tor_browser(page_titles)
151 page_titles = [page_titles] if page_titles.class == String
152 assert_equal(Array, page_titles.class)
153 if $language == 'German'
154 browser_name = 'Tor-Browser'
155 reload_action = 'Neu laden'
157 browser_name = 'Tor Browser'
158 reload_action = 'Reload'
161 # The 'Reload' button (graphically shown as a looping arrow)
162 # is only shown when a page has loaded, so once we see the
163 # expected title *and* this button has appeared, then we can be sure
164 # that the page has fully loaded.
165 @torbrowser.children(roleName: 'frame', showingOnly: true).any? do |frame|
167 .map { |page_title| "#{page_title} - #{browser_name}" }
168 .any? { |page_title| page_title == frame.name }
170 @torbrowser.child(reload_action, roleName: 'push button',
175 # This step is limited to the Tor Browser due to #7502 since dogtail
176 # uses the same interface.
177 Then /^"([^"]+)" has loaded in the Tor Browser$/ do |title|
178 page_has_loaded_in_the_tor_browser(title)
181 Then /^the (.*) has no plugins installed$/ do |browser|
182 step "I open the address \"about:plugins\" in the #{browser}"
183 step 'I see "TorBrowserNoPlugins.png" after at most 30 seconds'
186 def xul_app_shared_lib_check(pid, chroot, expected_absent_tbb_libs = [])
188 unwanted_native_libs = []
189 tbb_libs = $vm.execute_successfully("ls -1 #{chroot}${TBB_INSTALL}/*.so",
190 libs: 'tor-browser').stdout.split
191 firefox_pmap_info = $vm.execute("pmap --show-path #{pid}").stdout
192 tbb_libs.each do |lib|
193 lib_name = File.basename lib
194 absent_tbb_libs << lib_name unless /\W#{lib}$/.match(firefox_pmap_info)
195 native_libs = $vm.execute_successfully(
196 "find /usr/lib /lib -name \"#{lib_name}\""
198 native_libs.each do |native_lib|
199 if /\W#{native_lib}$"/.match(firefox_pmap_info)
200 unwanted_native_libs << lib_name
204 absent_tbb_libs -= expected_absent_tbb_libs
205 assert(absent_tbb_libs.empty? && unwanted_native_libs.empty?,
206 'The loaded shared libraries for the firefox process are not the ' \
207 "way we expect them.\n" \
208 "Expected TBB libs that are absent: #{absent_tbb_libs}\n" \
209 "Native libs that we don't want: #{unwanted_native_libs}")
212 Then /^the (.*) uses all expected TBB shared libraries$/ do |application|
213 info = xul_application_info(application)
214 pid = $vm.execute_successfully(
215 "pgrep --uid #{info[:user]} --full --exact '#{info[:cmd_regex]}'"
217 assert_match(/\A\d+\z/, pid, "It seems like #{application} is not running")
218 xul_app_shared_lib_check(pid, info[:chroot], info[:unused_tbb_libs])
221 Then /^the (.*) chroot is torn down$/ do |browser|
222 info = xul_application_info(browser)
223 try_for(30, msg: "The #{browser} chroot '#{info[:chroot]}' was " \
225 !$vm.execute("test -d '#{info[:chroot]}'").success?
229 Then /^the (.*) runs as the expected user$/ do |browser|
230 info = xul_application_info(browser)
231 assert_vmcommand_success(
232 $vm.execute("pgrep --full --exact '#{info[:cmd_regex]}'"),
233 "The #{browser} is not running"
235 assert_vmcommand_success(
237 "pgrep --uid #{info[:user]} --full --exact '#{info[:cmd_regex]}'"
239 "The #{browser} is not running as the #{info[:user]} user"
243 When /^I download some file in the Tor Browser$/ do
244 @some_file = 'tails-signing.key'
245 some_url = "https://tails.boum.org/#{@some_file}"
246 step "I open the address \"#{some_url}\" in the Tor Browser"
249 Then /^I get the browser download dialog$/ do
250 @screen.wait('BrowserDownloadDialog.png', 60)
251 @screen.wait('BrowserDownloadDialogSaveAsButton.png', 10)
254 When /^I save the file to the default Tor Browser download directory$/ do
255 @screen.click('BrowserDownloadDialogSaveAsButton.png')
256 @screen.wait('Gtk3SaveFileDialog.png', 10)
257 @screen.press('Return')
260 Then /^the file is saved to the default Tor Browser download directory$/ do
261 assert_not_nil(@some_file)
262 expected_path = "/home/#{LIVE_USER}/Tor Browser/#{@some_file}"
263 try_for(10) { $vm.file_exist?(expected_path) }
266 When /^I open the Tails homepage in the (.+)$/ do |browser|
267 step "I open the address \"https://tails.boum.org\" in the #{browser}"
270 Then /^the Tails homepage loads in the Unsafe Browser$/ do
271 @screen.wait('TailsHomepage.png', 60)
274 def headings_in_page(page_title)
275 @torbrowser.child(page_title, roleName: 'frame').children(roleName: 'heading')
278 def page_has_heading(page_title, heading)
279 headings_in_page(page_title).any? { |h| h.text == heading }
282 Then /^the Tor Browser shows the "([^"]+)" error$/ do |error|
284 page_has_heading('Problem loading page - Tor Browser', error)
288 Then /^Tor Browser displays a "([^"]+)" heading on the "([^"]+)" page$/ do |heading, page_title|
290 page_has_heading("#{page_title} - Tor Browser", heading)
294 Then /^Tor Browser displays a '([^']+)' heading on the "([^"]+)" page$/ do |heading, page_title|
296 page_has_heading("#{page_title} - Tor Browser", heading)
300 Then /^I can listen to an Ogg audio track in Tor Browser$/ do
301 test_url = 'https://archive.org/download/MussorgskyPicturesAtAnExhibitionorch.Ravel/09Mussorgsky_PicturesAtAnExhibition-LimogesTheMarketPlace.ogg'
302 info = xul_application_info('Tor Browser')
303 open_test_url = proc do
304 step "I open the address \"#{test_url}\" in the Tor Browser"
306 recovery_on_failure = proc do
307 @screen.press('Escape')
308 @screen.wait_vanish(info[:browser_stop_button_image], 3)
311 step 'no application is playing audio'
313 retry_tor(recovery_on_failure) do
314 step '1 application is playing audio after 30 seconds'
318 Then /^I can watch a WebM video in Tor Browser$/ do
319 test_url = WEBM_VIDEO_URL
320 info = xul_application_info('Tor Browser')
321 open_test_url = proc do
322 step "I open the address \"#{test_url}\" in the Tor Browser"
324 recovery_on_failure = proc do
325 @screen.press('Escape')
326 @screen.wait_vanish(info[:browser_stop_button_image], 3)
330 retry_tor(recovery_on_failure) do
331 @screen.wait('TorBrowserSampleRemoteWebMVideoFrame.png', 30)
335 Then /^DuckDuckGo is the default search engine$/ do
336 ddg_search_prompt = 'DuckDuckGoSearchPrompt.png'
338 when 'Arabic', 'Persian'
339 ddg_search_prompt = "DuckDuckGoSearchPromptRTL.png"
340 when 'Chinese', 'Hindi'
341 ddg_search_prompt = "DuckDuckGoSearchPrompt#{$language}.png"
343 step 'I start the Tor Browser'
344 step 'I open a new tab in the Tor Browser'
345 # Typing would require maintaining keymaps for every language in
346 # which we run this step ⇒ instead, paste the search string.
347 $vm.set_clipboard('a random search string')
348 @screen.press('ctrl', 'v')
349 @screen.wait(ddg_search_prompt, 20)
350 step 'I kill the Tor Browser'
353 Then(/^the screen keyboard works in Tor Browser$/) do
354 osk_key = 'ScreenKeyboardKeyX.png'
355 browser_bar_x = 'BrowserAddressBarX.png'
358 browser_bar_x = 'BrowserAddressBarXRTL.png'
359 when 'Chinese', 'Hindi'
360 browser_bar_x = "BrowserAddressBarX#{$language}.png"
362 osk_key = 'ScreenKeyboardKeyPersian.png'
363 browser_bar_x = 'BrowserAddressBarXPersian.png'
365 step 'I start the Tor Browser'
366 step 'I open a new tab in the Tor Browser'
367 @screen.wait('ScreenKeyboard.png', 10)
368 @screen.wait(osk_key, 10).click
369 @screen.wait(browser_bar_x, 20)
370 step 'I kill the Tor Browser'