Bug 757182 - Handle window.close in <iframe mozbrowser>. r=smaug
authorJustin Lebar <justin.lebar@gmail.com>
Thu, 07 Jun 2012 13:26:34 -0400
changeset 96090 0d138bc9b28ee02fb26a98741a4c6da42f60980f
parent 96089 8dc147df2d0f12861f7b1d6e9dbe2b28c7047803
child 96091 e11cfc5084eaf6f55386afdb1d668f874c595d0c
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerssmaug
bugs757182
milestone16.0a1
Bug 757182 - Handle window.close in <iframe mozbrowser>. r=smaug
dom/base/nsGlobalWindow.cpp
dom/browser-element/BrowserElementChild.js
dom/browser-element/BrowserElementParent.js
dom/browser-element/mochitest/Makefile.in
dom/browser-element/mochitest/test_browserFrameClose.html
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6356,17 +6356,22 @@ nsGlobalWindow::CanClose()
   return true;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::Close()
 {
   FORWARD_TO_OUTER(Close, (), NS_ERROR_NOT_INITIALIZED);
 
-  if (IsFrame() || !mDocShell || IsInModalState()) {
+  bool isMozBrowser = false;
+  if (mDocShell) {
+    mDocShell->GetIsBrowserFrame(&isMozBrowser);
+  }
+
+  if ((!isMozBrowser && IsFrame()) || !mDocShell || IsInModalState()) {
     // window.close() is called on a frame in a frameset, on a window
     // that's already closed, or on a window for which there's
     // currently a modal dialog open. Ignore such calls.
 
     return NS_OK;
   }
 
   if (mHavePendingClose) {
--- a/dom/browser-element/BrowserElementChild.js
+++ b/dom/browser-element/BrowserElementChild.js
@@ -3,16 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 let Cu = Components.utils;
 let Ci = Components.interfaces;
 let Cc = Components.classes;
 let Cr = Components.results;
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
 
 // Event whitelisted for bubbling.
 let whitelistedEvents = [
   Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE,   // Back button.
   Ci.nsIDOMKeyEvent.DOM_VK_CONTEXT_MENU,
@@ -42,20 +43,20 @@ function sendSyncMsg(msg, data) {
  *
  * Our job here is to listen for events within this frame and bubble them up to
  * the parent process.
  */
 
 var global = this;
 
 function BrowserElementChild() {
-  this._init();
-
   // Maps outer window id --> weak ref to window.  Used by modal dialog code.
   this._windowIDDict = {};
+
+  this._init();
 };
 
 BrowserElementChild.prototype = {
   _init: function() {
     debug("Starting up.");
     sendAsyncMsg("hello");
 
     BrowserElementPromptService.mapWindowToBrowserElementChild(content, this);
@@ -113,16 +114,19 @@ BrowserElementChild.prototype = {
                                this._keyEventHandler.bind(this),
                                /* useCapture = */ true);
     els.addSystemEventListener(global, 'keypress',
                                this._keyEventHandler.bind(this),
                                /* useCapture = */ true);
     els.addSystemEventListener(global, 'keyup',
                                this._keyEventHandler.bind(this),
                                /* useCapture = */ true);
+    els.addSystemEventListener(global, 'DOMWindowClose',
+                               this._closeHandler.bind(this),
+                               /* useCapture = */ false);
   },
 
   _tryGetInnerWindowID: function(win) {
     let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils);
     try {
       return utils.currentInnerWindowID;
     }
@@ -278,16 +282,29 @@ BrowserElementChild.prototype = {
         sendAsyncMsg('iconchange', e.target.href);
       }
       else {
         debug("Not top level!");
       }
     }
   },
 
+  _closeHandler: function(e) {
+    let win = e.target;
+    if (win != content || e.defaultPrevented) {
+      return;
+    }
+
+    debug("Closing window " + win);
+    sendAsyncMsg('close');
+
+    // Inform the window implementation that we handled this close ourselves.
+    e.preventDefault();
+  },
+
   _recvGetScreenshot: function(data) {
     debug("Received getScreenshot message: (" + data.json.id + ")");
     var canvas = content.document
       .createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     var ctx = canvas.getContext("2d");
     canvas.mozOpaque = true;
     canvas.height = content.innerHeight;
     canvas.width = content.innerWidth;
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -107,16 +107,17 @@ BrowserElementParent.prototype = {
     }
 
     addMessageListener("hello", this._recvHello);
     addMessageListener("locationchange", this._fireEventFromMsg);
     addMessageListener("loadstart", this._fireEventFromMsg);
     addMessageListener("loadend", this._fireEventFromMsg);
     addMessageListener("titlechange", this._fireEventFromMsg);
     addMessageListener("iconchange", this._fireEventFromMsg);
+    addMessageListener("close", this._fireEventFromMsg);
     addMessageListener("get-mozapp-manifest-url", this._sendMozAppManifestURL);
     addMessageListener("keyevent", this._fireKeyEvent);
     addMessageListener("showmodalprompt", this._handleShowModalPrompt);
     mm.addMessageListener('browser-element-api:got-screenshot',
                           this._recvGotScreenshot.bind(this));
 
     XPCNativeWrapper.unwrap(frameElement).getScreenshot =
       this._getScreenshot.bind(this, mm, frameElement);
--- a/dom/browser-element/mochitest/Makefile.in
+++ b/dom/browser-element/mochitest/Makefile.in
@@ -25,12 +25,13 @@ include $(topsrcdir)/config/rules.mk
 		test_browserFrame7.html \
 		test_browserFrame8.html \
 		test_browserFrame9.html \
 		test_browserFrame10.html \
 		test_browserFrame_keyEvents.html \
 		test_browserFrameAlert.html \
 		test_browserFramePromptCheck.html \
 		test_browserFramePromptConfirm.html \
+		test_browserFrameClose.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserFrameClose.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=757182
+-->
+<head>
+  <title>Test for Bug 757182</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserFrameHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=757182">Mozilla Bug 757182</a>
+
+<!--
+  Test that window.close() works.
+-->
+
+<script type="application/javascript;version=1.7">
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+  browserFrameHelpers.setEnabledPref(true);
+  browserFrameHelpers.addToWhitelist();
+
+  var iframe = document.createElement('iframe');
+  iframe.mozbrowser = true;
+  document.body.appendChild(iframe);
+
+  iframe.addEventListener("mozbrowserclose", function(e) {
+    ok(true, "got mozbrowserclose event.");
+    SimpleTest.finish();
+  });
+
+  iframe.src = "data:text/html,<html><body><script>window.close()</scr"+"ipt></body></html>";
+}
+
+runTest();
+</script>
+</body>
+</html>