summaryrefslogtreecommitdiffstats
path: root/docshell/test/browser/browser_onbeforeunload_navigation.js
blob: d2feb69d4a28ac52cfefde7393f3baa76f328240 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
var contentWindow;
var originalLocation;
var currentTest = -1;
var stayingOnPage = true;

var TEST_PAGE = "http://mochi.test:8888/browser/docshell/test/browser/file_bug1046022.html";
var TARGETED_PAGE = "data:text/html," + encodeURIComponent("<body>Shouldn't be seeing this</body>");

SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});

var loadExpected = TEST_PAGE;
var testTab;
var testsLength;

var loadStarted = false;
var tabStateListener = {
  onStateChange: function(webprogress, request, stateFlags, status) {
    let startDocumentFlags = Ci.nsIWebProgressListener.STATE_START |
                             Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
    if ((stateFlags & startDocumentFlags) == startDocumentFlags) {
      loadStarted = true;
    }
  },
  onStatusChange: () => {},
  onLocationChange: () => {},
  onSecurityChange: () => {},
  onProgressChange: () => {},
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener])
};

function onTabLoaded(event) {
  info("A document loaded in a tab!");
  let loadedPage = event.target.location.href;
  if (loadedPage == "about:blank" ||
      event.originalTarget != testTab.linkedBrowser.contentDocument) {
    return;
  }

  if (!loadExpected) {
    ok(false, "Expected no page loads, but loaded " + loadedPage + " instead!");
    return;
  }

  if (!testsLength) {
    testsLength = testTab.linkedBrowser.contentWindow.wrappedJSObject.testFns.length;
  }

  is(loadedPage, loadExpected, "Loaded the expected page");
  if (contentWindow) {
    is(contentWindow.document, event.target, "Same doc");
  }
  if (onAfterPageLoad) {
    onAfterPageLoad();
  }
}

function onAfterTargetedPageLoad() {
  ok(!stayingOnPage, "We should only fire if we're expecting to let the onbeforeunload dialog proceed to the new location");
  is(testTab.linkedBrowser.currentURI.spec, TARGETED_PAGE, "Should have loaded the expected new page");

  runNextTest();
}

function onTabModalDialogLoaded(node) {
  let content = testTab.linkedBrowser.contentWindow;
  ok(!loadStarted, "No load should be started.");
  info(content.location.href);
  is(content, contentWindow, "Window should be the same still.");
  is(content.location.href, originalLocation, "Page should not have changed.");
  is(content.mySuperSpecialMark, 42, "Page should not have refreshed.");

  ok(!content.dialogWasInvoked, "Dialog should only be invoked once per test.");
  content.dialogWasInvoked = true;


  // Now listen for the dialog going away again...
  let observer = new MutationObserver(function(muts) {
    if (!node.parentNode) {
      info("Dialog is gone");
      observer.disconnect();
      observer = null;
      // If we're staying on the page, run the next test from here
      if (stayingOnPage) {
        // Evil, but necessary: without this delay, we manage to still break our
        // own onbeforeunload code, because we'll basically cause a new load to be
        // started while processing the destruction of the dialog for the old one.
        executeSoon(runNextTest);
      }
      // if we accepted a page load in the dialog, the next test will get started
      // by the load handler for that page loading
    }
  });
  observer.observe(node.parentNode, {childList: true});

  // If we're going to let the page load, set us up to listen for that happening:
  if (!stayingOnPage) {
    loadExpected = TARGETED_PAGE;
    onAfterPageLoad = onAfterTargetedPageLoad;
  }

  let button = stayingOnPage ? node.ui.button1 : node.ui.button0;
  // ... and then actually make the dialog go away
  info("Clicking button: " + button.label);
  EventUtils.synthesizeMouseAtCenter(button, {});
}

// Listen for the dialog being created
Services.obs.addObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded", false);

function runNextTest() {
  currentTest++;
  if (currentTest >= testsLength) {
    if (!stayingOnPage) {
      finish();
      return;
    }
    // Run the same tests again, but this time let the navigation happen:
    stayingOnPage = false;
    // Remove onbeforeunload handler, or this load will trigger the dialog...
    contentWindow.onbeforeunload = null;
    currentTest = 0;
  }


  if (!stayingOnPage) {
    // Right now we're on the data: page. Null contentWindow out to
    // avoid CPOW errors when contentWindow is no longer the correct
    // outer window proxy object.
    contentWindow = null;

    onAfterPageLoad = runCurrentTest;
    loadExpected = TEST_PAGE;
    testTab.linkedBrowser.loadURI(TEST_PAGE);
  } else {
    runCurrentTest();
  }
}

function runCurrentTest() {
  // Reset things so we're sure the previous tests failings don't influence this one:
  contentWindow = testTab.linkedBrowser.contentWindow;
  contentWindow.mySuperSpecialMark = 42;
  contentWindow.dialogWasInvoked = false;
  originalLocation = contentWindow.location.href;
  // And run this test:
  info("Running test with onbeforeunload " + contentWindow.wrappedJSObject.testFns[currentTest].toSource());
  contentWindow.onbeforeunload = contentWindow.wrappedJSObject.testFns[currentTest];
  loadStarted = false;
  testTab.linkedBrowser.loadURI(TARGETED_PAGE);
}

var onAfterPageLoad = runNextTest;

function test() {
  waitForExplicitFinish();
  gBrowser.addProgressListener(tabStateListener);

  testTab = gBrowser.selectedTab = gBrowser.addTab();
  testTab.linkedBrowser.addEventListener("load", onTabLoaded, true);
  testTab.linkedBrowser.loadURI(TEST_PAGE);
}

registerCleanupFunction(function() {
  // Remove the handler, or closing this tab will prove tricky:
  if (contentWindow) {
    try {
      contentWindow.onbeforeunload = null;
    } catch (ex) {}
  }
  contentWindow = null;
  testTab.linkedBrowser.removeEventListener("load", onTabLoaded, true);
  Services.obs.removeObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded");
  gBrowser.removeProgressListener(tabStateListener);
  gBrowser.removeTab(testTab);
});