1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5from __future__ import absolute_import, print_function
6
7import contextlib
8import os
9
10from six.moves.urllib.parse import quote
11
12from marionette_driver import By, errors, expected, Wait
13from marionette_driver.keys import Keys
14from marionette_driver.marionette import Alert
15from marionette_harness import (
16    MarionetteTestCase,
17    run_if_manage_instance,
18    skip_unless_browser_pref,
19    WindowManagerMixin,
20)
21
22here = os.path.abspath(os.path.dirname(__file__))
23
24
25BLACK_PIXEL = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="  # noqa
26RED_PIXEL = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlPM0jRW/QAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII="  # noqa
27
28
29def inline(doc):
30    return "data:text/html;charset=utf-8,%s" % quote(doc)
31
32
33def inline_image(data):
34    return "data:image/png;base64,%s" % data
35
36
37class BaseNavigationTestCase(WindowManagerMixin, MarionetteTestCase):
38    def setUp(self):
39        super(BaseNavigationTestCase, self).setUp()
40
41        file_path = os.path.join(here, "data", "test.html").replace("\\", "/")
42
43        self.test_page_file_url = "file:///{}".format(file_path)
44        self.test_page_frameset = self.marionette.absolute_url("frameset.html")
45        self.test_page_insecure = self.fixtures.where_is("test.html", on="https")
46        self.test_page_not_remote = "about:robots"
47        self.test_page_push_state = self.marionette.absolute_url(
48            "navigation_pushstate.html"
49        )
50        self.test_page_remote = self.marionette.absolute_url("test.html")
51        self.test_page_slow_resource = self.marionette.absolute_url(
52            "slow_resource.html"
53        )
54
55        if self.marionette.session_capabilities["platformName"] == "mac":
56            self.mod_key = Keys.META
57        else:
58            self.mod_key = Keys.CONTROL
59
60        # Always use a blank new tab for an empty history
61        self.new_tab = self.open_tab()
62        self.marionette.switch_to_window(self.new_tab)
63        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
64            lambda _: self.history_length == 1,
65            message="The newly opened tab doesn't have a browser history length of 1",
66        )
67
68    def tearDown(self):
69        self.marionette.timeout.reset()
70
71        self.close_all_tabs()
72
73        super(BaseNavigationTestCase, self).tearDown()
74
75    @property
76    def history_length(self):
77        return self.marionette.execute_script("return window.history.length;")
78
79    @property
80    def is_remote_tab(self):
81        with self.marionette.using_context("chrome"):
82            # TODO: DO NOT USE MOST RECENT WINDOW BUT CURRENT ONE
83            return self.marionette.execute_script(
84                """
85              Components.utils.import("resource://gre/modules/AppConstants.jsm");
86
87              let win = null;
88
89              if (AppConstants.MOZ_APP_NAME == "fennec") {
90                Components.utils.import("resource://gre/modules/Services.jsm");
91                win = Services.wm.getMostRecentWindow("navigator:browser");
92              } else {
93                Components.utils.import("resource:///modules/BrowserWindowTracker.jsm");
94                win = BrowserWindowTracker.getTopWindow();
95              }
96
97              let tabBrowser = null;
98
99              // Fennec
100              if (win.BrowserApp) {
101                tabBrowser = win.BrowserApp.selectedBrowser;
102
103              // Firefox
104              } else if (win.gBrowser) {
105                tabBrowser = win.gBrowser.selectedBrowser;
106
107              } else {
108                return null;
109              }
110
111              return tabBrowser.isRemoteBrowser;
112            """
113            )
114
115    @property
116    def ready_state(self):
117        return self.marionette.execute_script(
118            "return window.document.readyState;", sandbox=None
119        )
120
121
122class TestNavigate(BaseNavigationTestCase):
123    def test_set_location_through_execute_script(self):
124        # To avoid unexpected remoteness changes and a hang in any non-navigation
125        # command (bug 1519354) when navigating via the location bar, already
126        # pre-load a page which causes a remoteness change.
127        self.marionette.navigate(self.test_page_push_state)
128
129        self.marionette.execute_script(
130            "window.location.href = arguments[0];",
131            script_args=(self.test_page_remote,),
132            sandbox=None,
133        )
134
135        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
136            expected.element_present(*(By.ID, "testh1")),
137            message="Target element 'testh1' has not been found",
138        )
139
140        self.assertEqual(self.test_page_remote, self.marionette.get_url())
141
142    def test_navigate_chrome_unsupported_error(self):
143        with self.marionette.using_context("chrome"):
144            self.assertRaises(
145                errors.UnsupportedOperationException,
146                self.marionette.navigate,
147                "about:blank",
148            )
149            self.assertRaises(
150                errors.UnsupportedOperationException, self.marionette.go_back
151            )
152            self.assertRaises(
153                errors.UnsupportedOperationException, self.marionette.go_forward
154            )
155            self.assertRaises(
156                errors.UnsupportedOperationException, self.marionette.refresh
157            )
158
159    def test_get_current_url_returns_top_level_browsing_context_url(self):
160        page_iframe = self.marionette.absolute_url("test_iframe.html")
161
162        self.marionette.navigate(page_iframe)
163        self.assertEqual(page_iframe, self.marionette.get_url())
164        frame = self.marionette.find_element(By.CSS_SELECTOR, "#test_iframe")
165        self.marionette.switch_to_frame(frame)
166        self.assertEqual(page_iframe, self.marionette.get_url())
167
168    def test_get_current_url(self):
169        self.marionette.navigate(self.test_page_remote)
170        self.assertEqual(self.test_page_remote, self.marionette.get_url())
171        self.marionette.navigate("about:blank")
172        self.assertEqual("about:blank", self.marionette.get_url())
173
174    def test_navigate_in_child_frame_changes_to_top(self):
175        self.marionette.navigate(self.test_page_frameset)
176        frame = self.marionette.find_element(By.NAME, "third")
177        self.marionette.switch_to_frame(frame)
178        self.assertRaises(
179            errors.NoSuchElementException,
180            self.marionette.find_element,
181            By.NAME,
182            "third",
183        )
184
185        self.marionette.navigate(self.test_page_frameset)
186        self.marionette.find_element(By.NAME, "third")
187
188    def test_invalid_url(self):
189        with self.assertRaises(errors.MarionetteException):
190            self.marionette.navigate("foo")
191        with self.assertRaises(errors.MarionetteException):
192            self.marionette.navigate("thisprotocoldoesnotexist://")
193
194    def test_find_element_state_complete(self):
195        self.marionette.navigate(self.test_page_remote)
196        self.assertEqual("complete", self.ready_state)
197        self.assertTrue(self.marionette.find_element(By.ID, "mozLink"))
198
199    def test_navigate_timeout_error_no_remoteness_change(self):
200        is_remote_before_timeout = self.is_remote_tab
201        self.marionette.timeout.page_load = 0.5
202        with self.assertRaises(errors.TimeoutException):
203            self.marionette.navigate(self.marionette.absolute_url("slow"))
204        self.assertEqual(self.is_remote_tab, is_remote_before_timeout)
205
206    def test_navigate_timeout_error_remoteness_change(self):
207        self.assertTrue(self.is_remote_tab)
208        self.marionette.navigate("about:robots")
209        self.assertFalse(self.is_remote_tab)
210
211        self.marionette.timeout.page_load = 0.5
212        with self.assertRaises(errors.TimeoutException):
213            self.marionette.navigate(self.marionette.absolute_url("slow"))
214
215    def test_navigate_to_same_image_document_twice(self):
216        self.marionette.navigate(self.fixtures.where_is("black.png"))
217        self.assertIn("black.png", self.marionette.title)
218        self.marionette.navigate(self.fixtures.where_is("black.png"))
219        self.assertIn("black.png", self.marionette.title)
220
221    def test_navigate_hash_change(self):
222        doc = inline("<p id=foo>")
223        self.marionette.navigate(doc)
224        self.marionette.execute_script("window.visited = true", sandbox=None)
225        self.marionette.navigate("{}#foo".format(doc))
226        self.assertTrue(
227            self.marionette.execute_script("return window.visited", sandbox=None)
228        )
229
230    def test_navigate_hash_argument_identical(self):
231        test_page = "{}#foo".format(inline("<p id=foo>"))
232
233        self.marionette.navigate(test_page)
234        self.marionette.find_element(By.ID, "foo")
235        self.marionette.navigate(test_page)
236        self.marionette.find_element(By.ID, "foo")
237
238    def test_navigate_hash_argument_differnt(self):
239        test_page = "{}#Foo".format(inline("<p id=foo>"))
240
241        self.marionette.navigate(test_page)
242        self.marionette.find_element(By.ID, "foo")
243        self.marionette.navigate(test_page.lower())
244        self.marionette.find_element(By.ID, "foo")
245
246    def test_navigate_history_pushstate(self):
247        target_page = self.marionette.absolute_url("navigation_pushstate_target.html")
248
249        self.marionette.navigate(self.test_page_push_state)
250        self.marionette.find_element(By.ID, "forward").click()
251
252        # By using pushState() the URL is updated but the target page is not loaded
253        # and as such the element is not displayed
254        self.assertEqual(self.marionette.get_url(), target_page)
255        with self.assertRaises(errors.NoSuchElementException):
256            self.marionette.find_element(By.ID, "target")
257
258        self.marionette.go_back()
259        self.assertEqual(self.marionette.get_url(), self.test_page_push_state)
260
261        # The target page still gets not loaded
262        self.marionette.go_forward()
263        self.assertEqual(self.marionette.get_url(), target_page)
264        with self.assertRaises(errors.NoSuchElementException):
265            self.marionette.find_element(By.ID, "target")
266
267        # Navigating to a different page, and returning to the injected
268        # page, it will be loaded.
269        self.marionette.navigate(self.test_page_remote)
270        self.assertEqual(self.marionette.get_url(), self.test_page_remote)
271
272        self.marionette.go_back()
273        self.assertEqual(self.marionette.get_url(), target_page)
274        self.marionette.find_element(By.ID, "target")
275
276        self.marionette.go_back()
277        self.assertEqual(self.marionette.get_url(), self.test_page_push_state)
278
279    def test_navigate_file_url(self):
280        self.marionette.navigate(self.test_page_file_url)
281        self.marionette.find_element(By.ID, "file-url")
282        self.marionette.navigate(self.test_page_remote)
283
284    def test_navigate_file_url_remoteness_change(self):
285        self.marionette.navigate("about:robots")
286        self.assertFalse(self.is_remote_tab)
287
288        self.marionette.navigate(self.test_page_file_url)
289        self.assertTrue(self.is_remote_tab)
290        self.marionette.find_element(By.ID, "file-url")
291
292        self.marionette.navigate("about:robots")
293        self.assertFalse(self.is_remote_tab)
294
295    def test_stale_element_after_remoteness_change(self):
296        self.marionette.navigate(self.test_page_file_url)
297        self.assertTrue(self.is_remote_tab)
298        elem = self.marionette.find_element(By.ID, "file-url")
299
300        self.marionette.navigate("about:robots")
301        self.assertFalse(self.is_remote_tab)
302
303        # Force a GC to get rid of the replaced browsing context.
304        with self.marionette.using_context("chrome"):
305            self.marionette.execute_async_script(
306                """
307                const resolve = arguments[0];
308
309                var memSrv = Cc["@mozilla.org/memory-reporter-manager;1"]
310                  .getService(Ci.nsIMemoryReporterManager);
311
312                Services.obs.notifyObservers(null, "child-mmu-request", null);
313                memSrv.minimizeMemoryUsage(resolve);
314            """
315            )
316
317        with self.assertRaises(errors.StaleElementException):
318            elem.click()
319
320    def test_about_blank_for_new_docshell(self):
321        self.assertEqual(self.marionette.get_url(), "about:blank")
322
323        self.marionette.navigate("about:blank")
324
325    def test_about_newtab(self):
326        with self.marionette.using_prefs({"browser.newtabpage.enabled": True}):
327            self.marionette.navigate("about:newtab")
328
329            self.marionette.navigate(self.test_page_remote)
330            self.marionette.find_element(By.ID, "testDiv")
331
332    @run_if_manage_instance("Only runnable if Marionette manages the instance")
333    def test_focus_after_navigation(self):
334        self.marionette.restart()
335
336        self.marionette.navigate(inline("<input autofocus>"))
337        focus_el = self.marionette.find_element(By.CSS_SELECTOR, ":focus")
338        self.assertEqual(self.marionette.get_active_element(), focus_el)
339
340    def test_no_hang_when_navigating_after_closing_original_tab(self):
341        # Close the start tab
342        self.marionette.switch_to_window(self.start_tab)
343        self.marionette.close()
344
345        self.marionette.switch_to_window(self.new_tab)
346        self.marionette.navigate(self.test_page_remote)
347
348    def test_type_to_non_remote_tab(self):
349        self.marionette.navigate(self.test_page_not_remote)
350        self.assertFalse(self.is_remote_tab)
351
352        with self.marionette.using_context("chrome"):
353            urlbar = self.marionette.find_element(By.ID, "urlbar-input")
354            urlbar.send_keys(self.mod_key + "a")
355            urlbar.send_keys(self.mod_key + "x")
356            urlbar.send_keys("about:support" + Keys.ENTER)
357
358        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
359            lambda mn: mn.get_url() == "about:support",
360            message="'about:support' hasn't been loaded",
361        )
362        self.assertFalse(self.is_remote_tab)
363
364    def test_type_to_remote_tab(self):
365        self.assertTrue(self.is_remote_tab)
366
367        with self.marionette.using_context("chrome"):
368            urlbar = self.marionette.find_element(By.ID, "urlbar-input")
369            urlbar.send_keys(self.mod_key + "a")
370            urlbar.send_keys(self.mod_key + "x")
371            urlbar.send_keys(self.test_page_remote + Keys.ENTER)
372
373        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
374            lambda mn: mn.get_url() == self.test_page_remote,
375            message="'{}' hasn't been loaded".format(self.test_page_remote),
376        )
377        self.assertTrue(self.is_remote_tab)
378
379
380class TestBackForwardNavigation(BaseNavigationTestCase):
381    def run_bfcache_test(self, test_pages):
382        # Helper method to run simple back and forward testcases.
383
384        def check_page_status(page, expected_history_length):
385            if "alert_text" in page:
386                if page["alert_text"] is None:
387                    # navigation auto-dismisses beforeunload prompt
388                    with self.assertRaises(errors.NoAlertPresentException):
389                        Alert(self.marionette).text
390                else:
391                    self.assertEqual(Alert(self.marionette).text, page["alert_text"])
392
393            self.assertEqual(self.marionette.get_url(), page["url"])
394            self.assertEqual(self.history_length, expected_history_length)
395
396            if "is_remote" in page:
397                self.assertEqual(
398                    page["is_remote"],
399                    self.is_remote_tab,
400                    "'{}' doesn't match expected remoteness state: {}".format(
401                        page["url"], page["is_remote"]
402                    ),
403                )
404
405            if "callback" in page and callable(page["callback"]):
406                page["callback"]()
407
408        for index, page in enumerate(test_pages):
409            if "error" in page:
410                with self.assertRaises(page["error"]):
411                    self.marionette.navigate(page["url"])
412            else:
413                self.marionette.navigate(page["url"])
414
415            check_page_status(page, index + 1)
416
417        # Now going back in history for all test pages by backward iterating
418        # through the list (-1) and skipping the first entry at the end (-2).
419        for page in test_pages[-2::-1]:
420            if "error" in page:
421                with self.assertRaises(page["error"]):
422                    self.marionette.go_back()
423            else:
424                self.marionette.go_back()
425
426            check_page_status(page, len(test_pages))
427
428        # Now going forward in history by skipping the first entry.
429        for page in test_pages[1::]:
430            if "error" in page:
431                with self.assertRaises(page["error"]):
432                    self.marionette.go_forward()
433            else:
434                self.marionette.go_forward()
435
436            check_page_status(page, len(test_pages))
437
438    def test_no_history_items(self):
439        # Both methods should not raise a failure if no navigation is possible
440        self.marionette.go_back()
441        self.marionette.go_forward()
442
443    def test_dismissed_beforeunload_prompt(self):
444        url_beforeunload = inline(
445            """
446          <input type="text">
447          <script>
448            window.addEventListener("beforeunload", function (event) {
449              event.preventDefault();
450            });
451          </script>
452        """
453        )
454
455        def modify_page():
456            self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
457
458        test_pages = [
459            {"url": inline("<p>foobar</p>"), "alert_text": None},
460            {"url": url_beforeunload, "callback": modify_page},
461            {"url": inline("<p>foobar</p>"), "alert_text": None},
462        ]
463
464        self.run_bfcache_test(test_pages)
465
466    def test_data_urls(self):
467        test_pages = [
468            {"url": inline("<p>foobar</p>")},
469            {"url": self.test_page_remote},
470            {"url": inline("<p>foobar</p>")},
471        ]
472        self.run_bfcache_test(test_pages)
473
474    def test_same_document_hash_change(self):
475        test_pages = [
476            {"url": "{}#23".format(self.test_page_remote)},
477            {"url": self.test_page_remote},
478            {"url": "{}#42".format(self.test_page_remote)},
479        ]
480        self.run_bfcache_test(test_pages)
481
482    def test_file_url(self):
483        test_pages = [
484            {"url": self.test_page_remote},
485            {"url": self.test_page_file_url},
486            {"url": self.test_page_remote},
487        ]
488        self.run_bfcache_test(test_pages)
489
490    def test_frameset(self):
491        test_pages = [
492            {"url": self.marionette.absolute_url("frameset.html")},
493            {"url": self.test_page_remote},
494            {"url": self.marionette.absolute_url("frameset.html")},
495        ]
496        self.run_bfcache_test(test_pages)
497
498    def test_frameset_after_navigating_in_frame(self):
499        test_element_locator = (By.ID, "email")
500
501        self.marionette.navigate(self.test_page_remote)
502        self.assertEqual(self.marionette.get_url(), self.test_page_remote)
503        self.assertEqual(self.history_length, 1)
504        page = self.marionette.absolute_url("frameset.html")
505        self.marionette.navigate(page)
506        self.assertEqual(self.marionette.get_url(), page)
507        self.assertEqual(self.history_length, 2)
508        frame = self.marionette.find_element(By.ID, "fifth")
509        self.marionette.switch_to_frame(frame)
510        link = self.marionette.find_element(By.ID, "linkId")
511        link.click()
512
513        # We cannot use get_url() to wait until the target page has been loaded,
514        # because it will return the URL of the top browsing context and doesn't
515        # wait for the page load to be complete.
516        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
517            expected.element_present(*test_element_locator),
518            message="Target element 'email' has not been found",
519        )
520        self.assertEqual(self.history_length, 3)
521
522        # Go back to the frame the click navigated away from
523        self.marionette.go_back()
524        self.assertEqual(self.marionette.get_url(), page)
525        with self.assertRaises(errors.NoSuchElementException):
526            self.marionette.find_element(*test_element_locator)
527
528        # Go back to the non-frameset page
529        self.marionette.switch_to_parent_frame()
530        self.marionette.go_back()
531        self.assertEqual(self.marionette.get_url(), self.test_page_remote)
532
533        # Go forward to the frameset page
534        self.marionette.go_forward()
535        self.assertEqual(self.marionette.get_url(), page)
536
537        # Go forward to the frame the click navigated to
538        # TODO: See above for automatic browser context switches. Hard to do here
539        frame = self.marionette.find_element(By.ID, "fifth")
540        self.marionette.switch_to_frame(frame)
541        self.marionette.go_forward()
542        self.marionette.find_element(*test_element_locator)
543        self.assertEqual(self.marionette.get_url(), page)
544
545    def test_image_to_html_to_image(self):
546        test_pages = [
547            {"url": self.marionette.absolute_url("black.png")},
548            {"url": self.test_page_remote},
549            {"url": self.marionette.absolute_url("white.png")},
550        ]
551        self.run_bfcache_test(test_pages)
552
553    def test_image_to_image(self):
554        test_pages = [
555            {"url": self.marionette.absolute_url("black.png")},
556            {"url": self.marionette.absolute_url("white.png")},
557            {"url": inline_image(RED_PIXEL)},
558            {"url": inline_image(BLACK_PIXEL)},
559            {"url": self.marionette.absolute_url("black.png")},
560        ]
561        self.run_bfcache_test(test_pages)
562
563    def test_remoteness_change(self):
564        test_pages = [
565            {"url": "about:robots", "is_remote": False},
566            {"url": self.test_page_remote, "is_remote": True},
567            {"url": "about:robots", "is_remote": False},
568        ]
569        self.run_bfcache_test(test_pages)
570
571    def test_non_remote_about_pages(self):
572        test_pages = [
573            {"url": "about:preferences", "is_remote": False},
574            {"url": "about:robots", "is_remote": False},
575            {"url": "about:support", "is_remote": False},
576        ]
577        self.run_bfcache_test(test_pages)
578
579    def test_navigate_to_requested_about_page_after_error_page(self):
580        test_pages = [
581            {"url": "about:neterror"},
582            {"url": self.test_page_remote},
583            {"url": "about:blocked"},
584        ]
585        self.run_bfcache_test(test_pages)
586
587    def test_timeout_error(self):
588        urls = [
589            self.marionette.absolute_url("slow?delay=3"),
590            self.test_page_remote,
591            self.marionette.absolute_url("slow?delay=4"),
592        ]
593
594        # First, load all pages completely to get them added to the cache
595        for index, url in enumerate(urls):
596            self.marionette.navigate(url)
597            self.assertEqual(url, self.marionette.get_url())
598            self.assertEqual(self.history_length, index + 1)
599
600        self.marionette.go_back()
601        self.assertEqual(urls[1], self.marionette.get_url())
602
603        # Force triggering a timeout error
604        self.marionette.timeout.page_load = 0.5
605        with self.assertRaises(errors.TimeoutException):
606            self.marionette.go_back()
607        self.marionette.timeout.reset()
608
609        delay = Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
610            expected.element_present(By.ID, "delay"),
611            message="Target element 'delay' has not been found after timeout in 'back'",
612        )
613        self.assertEqual(delay.text, "3")
614
615        self.marionette.go_forward()
616        self.assertEqual(urls[1], self.marionette.get_url())
617
618        # Force triggering a timeout error
619        self.marionette.timeout.page_load = 0.5
620        with self.assertRaises(errors.TimeoutException):
621            self.marionette.go_forward()
622        self.marionette.timeout.reset()
623
624        delay = Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
625            expected.element_present(By.ID, "delay"),
626            message="Target element 'delay' has not been found after timeout in 'forward'",
627        )
628        self.assertEqual(delay.text, "4")
629
630    def test_certificate_error(self):
631        test_pages = [
632            {
633                "url": self.test_page_insecure,
634                "error": errors.InsecureCertificateException,
635            },
636            {"url": self.test_page_remote},
637            {
638                "url": self.test_page_insecure,
639                "error": errors.InsecureCertificateException,
640            },
641        ]
642        self.run_bfcache_test(test_pages)
643
644
645class TestRefresh(BaseNavigationTestCase):
646    def test_basic(self):
647        self.marionette.navigate(self.test_page_remote)
648        self.assertEqual(self.test_page_remote, self.marionette.get_url())
649
650        self.marionette.execute_script(
651            """
652          let elem = window.document.createElement('div');
653          elem.id = 'someDiv';
654          window.document.body.appendChild(elem);
655        """
656        )
657        self.marionette.find_element(By.ID, "someDiv")
658
659        self.marionette.refresh()
660        self.assertEqual(self.test_page_remote, self.marionette.get_url())
661        with self.assertRaises(errors.NoSuchElementException):
662            self.marionette.find_element(By.ID, "someDiv")
663
664    def test_refresh_in_child_frame_navigates_to_top(self):
665        self.marionette.navigate(self.test_page_frameset)
666        self.assertEqual(self.test_page_frameset, self.marionette.get_url())
667
668        frame = self.marionette.find_element(By.NAME, "third")
669        self.marionette.switch_to_frame(frame)
670        self.assertRaises(
671            errors.NoSuchElementException,
672            self.marionette.find_element,
673            By.NAME,
674            "third",
675        )
676
677        self.marionette.refresh()
678        self.marionette.find_element(By.NAME, "third")
679
680    def test_file_url(self):
681        self.marionette.navigate(self.test_page_file_url)
682        self.assertEqual(self.test_page_file_url, self.marionette.get_url())
683
684        self.marionette.refresh()
685        self.assertEqual(self.test_page_file_url, self.marionette.get_url())
686
687    def test_dismissed_beforeunload_prompt(self):
688        self.marionette.navigate(
689            inline(
690                """
691          <input type="text">
692          <script>
693            window.addEventListener("beforeunload", function (event) {
694              event.preventDefault();
695            });
696          </script>
697        """
698            )
699        )
700        self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
701        self.marionette.refresh()
702
703        # navigation auto-dismisses beforeunload prompt
704        with self.assertRaises(errors.NoAlertPresentException):
705            Alert(self.marionette).text
706
707    def test_image(self):
708        image = self.marionette.absolute_url("black.png")
709
710        self.marionette.navigate(image)
711        self.assertEqual(image, self.marionette.get_url())
712
713        self.marionette.refresh()
714        self.assertEqual(image, self.marionette.get_url())
715
716    def test_history_pushstate(self):
717        target_page = self.marionette.absolute_url("navigation_pushstate_target.html")
718
719        self.marionette.navigate(self.test_page_push_state)
720        self.marionette.find_element(By.ID, "forward").click()
721
722        # By using pushState() the URL is updated but the target page is not loaded
723        # and as such the element is not displayed
724        self.assertEqual(self.marionette.get_url(), target_page)
725        with self.assertRaises(errors.NoSuchElementException):
726            self.marionette.find_element(By.ID, "target")
727
728        # Refreshing the target page will trigger a full page load.
729        self.marionette.refresh()
730        self.assertEqual(self.marionette.get_url(), target_page)
731        self.marionette.find_element(By.ID, "target")
732
733        self.marionette.go_back()
734        self.assertEqual(self.marionette.get_url(), self.test_page_push_state)
735
736    def test_timeout_error(self):
737        slow_page = self.marionette.absolute_url("slow?delay=3")
738
739        self.marionette.navigate(slow_page)
740        self.assertEqual(slow_page, self.marionette.get_url())
741
742        self.marionette.timeout.page_load = 0.5
743        with self.assertRaises(errors.TimeoutException):
744            self.marionette.refresh()
745        self.assertEqual(slow_page, self.marionette.get_url())
746
747    def test_insecure_error(self):
748        with self.assertRaises(errors.InsecureCertificateException):
749            self.marionette.navigate(self.test_page_insecure)
750        self.assertEqual(self.marionette.get_url(), self.test_page_insecure)
751
752        with self.assertRaises(errors.InsecureCertificateException):
753            self.marionette.refresh()
754
755
756class TestTLSNavigation(BaseNavigationTestCase):
757    insecure_tls = {"acceptInsecureCerts": True}
758    secure_tls = {"acceptInsecureCerts": False}
759
760    def setUp(self):
761        super(TestTLSNavigation, self).setUp()
762
763        self.test_page_insecure = self.fixtures.where_is("test.html", on="https")
764
765        self.marionette.delete_session()
766        self.capabilities = self.marionette.start_session(self.insecure_tls)
767
768    def tearDown(self):
769        try:
770            self.marionette.delete_session()
771            self.marionette.start_session()
772        except:
773            pass
774
775        super(TestTLSNavigation, self).tearDown()
776
777    @contextlib.contextmanager
778    def safe_session(self):
779        try:
780            self.capabilities = self.marionette.start_session(self.secure_tls)
781            self.assertFalse(self.capabilities["acceptInsecureCerts"])
782            # Always use a blank new tab for an empty history
783            self.new_tab = self.open_tab()
784            self.marionette.switch_to_window(self.new_tab)
785            Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
786                lambda _: self.history_length == 1,
787                message="The newly opened tab doesn't have a browser history length of 1",
788            )
789            yield self.marionette
790        finally:
791            self.close_all_tabs()
792            self.marionette.delete_session()
793
794    @contextlib.contextmanager
795    def unsafe_session(self):
796        try:
797            self.capabilities = self.marionette.start_session(self.insecure_tls)
798            self.assertTrue(self.capabilities["acceptInsecureCerts"])
799            # Always use a blank new tab for an empty history
800            self.new_tab = self.open_tab()
801            self.marionette.switch_to_window(self.new_tab)
802            Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
803                lambda _: self.history_length == 1,
804                message="The newly opened tab doesn't have a browser history length of 1",
805            )
806            yield self.marionette
807        finally:
808            self.close_all_tabs()
809            self.marionette.delete_session()
810
811    def test_navigate_by_command(self):
812        self.marionette.navigate(self.test_page_insecure)
813        self.assertIn("https", self.marionette.get_url())
814
815    def test_navigate_by_click(self):
816        link_url = self.test_page_insecure
817        self.marionette.navigate(
818            inline("<a href=%s>https is the future</a>" % link_url)
819        )
820        self.marionette.find_element(By.TAG_NAME, "a").click()
821        self.assertIn("https", self.marionette.get_url())
822
823    def test_deactivation(self):
824        invalid_cert_url = self.test_page_insecure
825
826        self.marionette.delete_session()
827
828        print("with safe session")
829        with self.safe_session() as session:
830            with self.assertRaises(errors.InsecureCertificateException):
831                session.navigate(invalid_cert_url)
832
833        print("with unsafe session")
834        with self.unsafe_session() as session:
835            session.navigate(invalid_cert_url)
836
837        print("with safe session again")
838        with self.safe_session() as session:
839            with self.assertRaises(errors.InsecureCertificateException):
840                session.navigate(invalid_cert_url)
841
842
843class TestPageLoadStrategy(BaseNavigationTestCase):
844    def tearDown(self):
845        self.marionette.delete_session()
846        self.marionette.start_session()
847
848        super(TestPageLoadStrategy, self).tearDown()
849
850    def test_none(self):
851        self.marionette.delete_session()
852        self.marionette.start_session({"pageLoadStrategy": "none"})
853
854        self.marionette.navigate(self.test_page_slow_resource)
855        Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
856            lambda _: self.marionette.get_url() == self.test_page_slow_resource,
857            message="Target page has not been loaded",
858        )
859        self.marionette.find_element(By.ID, "slow")
860
861    def test_none_with_new_session_waits_for_page_loaded(self):
862        self.marionette.delete_session()
863        self.marionette.start_session({"pageLoadStrategy": "none"})
864
865        # Navigate will return immediately.
866        self.marionette.navigate(self.test_page_slow_resource)
867
868        # Make sure that when creating a new session right away it waits
869        # until the page has been finished loading.
870        self.marionette.delete_session()
871        self.marionette.start_session()
872
873        self.assertEqual(self.test_page_slow_resource, self.marionette.get_url())
874        self.assertEqual("complete", self.ready_state)
875        self.marionette.find_element(By.ID, "slow")
876
877    def test_eager(self):
878        self.marionette.delete_session()
879        self.marionette.start_session({"pageLoadStrategy": "eager"})
880
881        self.marionette.navigate(self.test_page_slow_resource)
882        self.assertEqual("interactive", self.ready_state)
883        self.assertEqual(self.test_page_slow_resource, self.marionette.get_url())
884        self.marionette.find_element(By.ID, "slow")
885
886    def test_normal(self):
887        self.marionette.delete_session()
888        self.marionette.start_session({"pageLoadStrategy": "normal"})
889
890        self.marionette.navigate(self.test_page_slow_resource)
891        self.assertEqual(self.test_page_slow_resource, self.marionette.get_url())
892        self.assertEqual("complete", self.ready_state)
893        self.marionette.find_element(By.ID, "slow")
894
895    def test_strategy_after_remoteness_change(self):
896        """Bug 1378191 - Reset of capabilities after listener reload."""
897        self.marionette.delete_session()
898        self.marionette.start_session({"pageLoadStrategy": "eager"})
899
900        # Trigger a remoteness change which will reload the listener script
901        self.assertTrue(
902            self.is_remote_tab, "Initial tab doesn't have remoteness flag set"
903        )
904        self.marionette.navigate("about:robots")
905        self.assertFalse(self.is_remote_tab, "Tab has remoteness flag set")
906        self.marionette.navigate(self.test_page_slow_resource)
907        self.assertEqual("interactive", self.ready_state)
908