merge m-c to fx-team
authorTim Taubert <tim.taubert@gmx.de>
Fri, 09 Mar 2012 08:09:45 +0100
changeset 88524 85ba09eada587c5afdb3270853a34d8570e65399
parent 88521 c2d1c97a8a2fb6ae3102ef7403b5e3e95766f120 (current diff)
parent 88523 cef96693077797edc58e2f2a46f78d16f1f91f70 (diff)
child 88525 89d3250b701d293eba8f6bad3eed6802659155f3
child 88764 fef8ed335cd28a9a4295b89a3cd1624dbd2e04e3
push id22206
push usertim.taubert@gmx.de
push dateFri, 09 Mar 2012 07:10:35 +0000
treeherdermozilla-central@85ba09eada58 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge m-c to fx-team
--- a/browser/base/content/test/newtab/Makefile.in
+++ b/browser/base/content/test/newtab/Makefile.in
@@ -15,15 +15,16 @@ include $(topsrcdir)/config/rules.mk
 	browser_newtab_block.js \
 	browser_newtab_disable.js \
 	browser_newtab_drag_drop.js \
 	browser_newtab_drop_preview.js \
 	browser_newtab_private_browsing.js \
 	browser_newtab_reset.js \
 	browser_newtab_tabsync.js \
 	browser_newtab_unpin.js \
+	browser_newtab_bug722273.js \
 	browser_newtab_bug723102.js \
 	browser_newtab_bug723121.js \
 	head.js \
 	$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_bug722273.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const NOW = Date.now() * 1000;
+const URL = "http://fake-site.com/";
+
+let tmp = {};
+Cu.import("resource:///modules/NewTabUtils.jsm", tmp);
+Cc["@mozilla.org/moz/jssubscript-loader;1"]
+  .getService(Ci.mozIJSSubScriptLoader)
+  .loadSubScript("chrome://browser/content/sanitize.js", tmp);
+
+let {NewTabUtils, Sanitizer} = tmp;
+
+let bhist = Cc["@mozilla.org/browser/global-history;2"]
+  .getService(Ci.nsIBrowserHistory);
+
+function runTests() {
+  clearHistory();
+  fillHistory();
+  yield addNewTabPageTab();
+
+  is(cells[0].site.url, URL, "first site is our fake site");
+
+  let page = {
+    update: function () {
+      executeSoon(TestRunner.next);
+    },
+
+    observe: function () {}
+  };
+
+  NewTabUtils.allPages.register(page);
+  yield clearHistory();
+
+  NewTabUtils.allPages.unregister(page);
+  ok(!cells[0].site, "the fake site is gone");
+}
+
+function fillHistory() {
+  let uri = makeURI(URL);
+  for (let i = 59; i > 0; i--)
+    bhist.addPageWithDetails(uri, "fake site", NOW - i * 60 * 1000000);
+}
+
+function clearHistory() {
+  let s = new Sanitizer();
+  s.prefDomain = "privacy.cpd.";
+
+  let prefs = gPrefService.getBranch(s.prefDomain);
+  prefs.setBoolPref("history", true);
+  prefs.setBoolPref("downloads", false);
+  prefs.setBoolPref("cache", false);
+  prefs.setBoolPref("cookies", false);
+  prefs.setBoolPref("formdata", false);
+  prefs.setBoolPref("offlineApps", false);
+  prefs.setBoolPref("passwords", false);
+  prefs.setBoolPref("sessions", false);
+  prefs.setBoolPref("siteSettings", false);
+
+  s.sanitize();
+}
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -124,20 +124,21 @@ function addNewTabPageTab() {
 
   // Wait for the new tab page to be loaded.
   browser.addEventListener("load", function onLoad() {
     browser.removeEventListener("load", onLoad, true);
 
     cw = browser.contentWindow;
 
     if (NewTabUtils.allPages.enabled) {
-      cells = cw.gGrid.cells;
-
       // Continue when the link cache has been populated.
-      NewTabUtils.links.populateCache(TestRunner.next);
+      NewTabUtils.links.populateCache(function () {
+        cells = cw.gGrid.cells;
+        executeSoon(TestRunner.next);
+      });
     } else {
       TestRunner.next();
     }
 
   }, true);
 }
 
 /**
@@ -241,16 +242,18 @@ function unpinCell(aCell) {
 
 /**
  * Simulates a drop and drop operation.
  * @param aDropTarget the cell that is the drop target
  * @param aDragSource the cell that contains the dragged site (optional)
  */
 function simulateDrop(aDropTarget, aDragSource) {
   let event = {
+    clientX: 0,
+    clientY: 0,
     dataTransfer: {
       mozUserCancelled: false,
       setData: function () null,
       setDragImage: function () null,
       getData: function () "about:blank#99\nblank"
     }
   };
 
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -727,18 +727,36 @@ CssRuleView.prototype = {
     }
 
     this._elementStyle = new ElementStyle(aElement, this.store);
     this._elementStyle.onChanged = function() {
       this._changed();
     }.bind(this);
 
     this._createEditors();
+
+    // When creating a new property, we fake the normal property
+    // editor behavior (focusing a property's value after entering its
+    // name) by responding to the name's blur event, creating the
+    // value editor, and grabbing focus to the value editor.  But if
+    // focus has already moved to another document, we won't be able
+    // to move focus to the new editor.
+    // Create a focusable item at the end of the editors to catch these
+    // cases.
+    this._focusBackstop = createChild(this.element, "div", {
+      tabindex: 0,
+    });
+    this._backstopHandler = function() {
+      // If this item is actually focused long enough to get the focus
+      // event, allow focus to move on out of this document.
+      moveFocus(this.doc.defaultView, FOCUS_FORWARD);
+    }.bind(this);
+    this._focusBackstop.addEventListener("focus", this._backstopHandler, false);
   },
-  
+
   /**
    * Update the rules for the currently highlighted element.
    */
   nodeChanged: function CssRuleView_nodeChanged()
   {
     this._clearRules();
     this._elementStyle.populate();
     this._createEditors();
@@ -757,16 +775,22 @@ CssRuleView.prototype = {
   /**
    * Clear the rule view.
    */
   clear: function CssRuleView_clear()
   {
     this._clearRules();
     this._viewedElement = null;
     this._elementStyle = null;
+
+    if (this._focusBackstop) {
+      this._focusBackstop.removeEventListener("focus", this._backstopHandler, false);
+      this._backstopHandler = null;
+      this._focusBackstop = null;
+    }
   },
 
   /**
    * Called when the user has made changes to the ElementStyle.
    * Emits an event that clients can listen to.
    */
   _changed: function CssRuleView_changed()
   {
@@ -840,17 +864,16 @@ RuleEditor.prototype = {
 
     let selectors = createChild(header, "span", {
       class: "ruleview-selector",
       textContent: this.rule.selectorText
     });
 
     this.openBrace = createChild(header, "span", {
       class: "ruleview-ruleopen",
-      tabindex: "0",
       textContent: " {"
     });
 
     this.openBrace.addEventListener("click", function() {
       this.newProperty();
     }.bind(this), true);
 
     this.propertyList = createChild(code, "ul", {
--- a/browser/devtools/styleinspector/test/Makefile.in
+++ b/browser/devtools/styleinspector/test/Makefile.in
@@ -55,16 +55,17 @@ include $(topsrcdir)/config/rules.mk
   browser_bug_692400_element_style.js \
   browser_csslogic_inherited.js \
   browser_ruleview_editor.js \
   browser_ruleview_editor_changedvalues.js \
   browser_ruleview_inherit.js \
   browser_ruleview_manipulation.js \
   browser_ruleview_override.js \
   browser_ruleview_ui.js \
+  browser_ruleview_focus.js \
   browser_bug705707_is_content_stylesheet.js \
   browser_bug722196_property_view_media_queries.js \
   browser_bug722196_rule_view_media_queries.js \
   browser_bug_592743_specificity.js \
   head.js \
   $(NULL)
 
 _BROWSER_TEST_PAGES = \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_focus.js
@@ -0,0 +1,106 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that focus doesn't leave the style editor when adding a property
+// (bug 719916)
+
+let doc;
+let stylePanel;
+
+function waitForRuleView(aCallback)
+{
+  if (InspectorUI.ruleView) {
+    aCallback();
+    return;
+  }
+
+  let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject);
+  ruleViewFrame.addEventListener("load", function(evt) {
+    ruleViewFrame.removeEventListener(evt.type, arguments.callee, true);
+    executeSoon(function() {
+      aCallback();
+    });
+  }, true);
+}
+
+function waitForEditorFocus(aParent, aCallback)
+{
+  aParent.addEventListener("focus", function onFocus(evt) {
+    if (evt.target.inplaceEditor) {
+      aParent.removeEventListener("focus", onFocus, true);
+      let editor = evt.target.inplaceEditor;
+      executeSoon(function() {
+        aCallback(editor);
+      });
+    }
+  }, true);
+}
+
+function openRuleView()
+{
+  Services.obs.addObserver(function onOpened() {
+    Services.obs.removeObserver(onOpened,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+
+    // Highlight a node.
+    let node = content.document.getElementsByTagName("h1")[0];
+    InspectorUI.inspectNode(node);
+    InspectorUI.stopInspecting();
+
+    // Open the rule view sidebar.
+    waitForRuleView(testFocus);
+
+    InspectorUI.showSidebar();
+    InspectorUI.ruleButton.click();
+
+    testFocus();
+  }, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  InspectorUI.openInspectorUI();
+}
+
+function testFocus()
+{
+  let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject);
+  let brace = ruleViewFrame.contentDocument.querySelectorAll(".ruleview-ruleclose")[0];
+  waitForEditorFocus(brace.parentNode, function onNewElement(aEditor) {
+    aEditor.input.value = "color";
+    waitForEditorFocus(brace.parentNode, function onEditingValue(aEditor) {
+      // If we actually get this focus we're ok.
+      ok(true, "We got focus.");
+      aEditor.input.value = "green";
+
+      // If we've retained focus, pressing return will start a new editor.
+      // If not, we'll wait here until we time out.
+      waitForEditorFocus(brace.parentNode, function onNewEditor(aEditor) {
+        aEditor.input.blur();
+        finishTest();
+      });
+      EventUtils.sendKey("return");
+    });
+    EventUtils.sendKey("return");
+  });
+
+  brace.focus();
+}
+
+function finishUp()
+{
+  doc = stylePanel = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
+    doc = content.document;
+    doc.title = "Rule View Test";
+    waitForFocus(openRuleView, content);
+  }, true);
+
+  content.location = "data:text/html,<h1>Some header text</h1>";
+}
--- a/browser/modules/NewTabUtils.jsm
+++ b/browser/modules/NewTabUtils.jsm
@@ -116,18 +116,16 @@ let Storage = {
       // Reset to normal DOM storage.
       this.currentStorage = this.domStorage;
 
       // When switching back from private browsing we need to reset the
       // grid and re-read its values from the underlying storage. We don't
       // want any data from private browsing to show up.
       PinnedLinks.resetCache();
       BlockedLinks.resetCache();
-
-      Pages.update();
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference])
 };
 
 /**
@@ -183,37 +181,27 @@ PrivateBrowsingStorage.prototype = {
  */
 let AllPages = {
   /**
    * The array containing all active pages.
    */
   _pages: [],
 
   /**
-   * Tells whether we already added a preference observer.
-   */
-  _observing: false,
-
-  /**
    * Cached value that tells whether the New Tab Page feature is enabled.
    */
   _enabled: null,
 
   /**
    * Adds a page to the internal list of pages.
    * @param aPage The page to register.
    */
   register: function AllPages_register(aPage) {
     this._pages.push(aPage);
-
-    // Add the preference observer if we haven't already.
-    if (!this._observing) {
-      this._observing = true;
-      Services.prefs.addObserver(PREF_NEWTAB_ENABLED, this, true);
-    }
+    this._addObserver();
   },
 
   /**
    * Removes a page from the internal list of pages.
    * @param aPage The page to unregister.
    */
   unregister: function AllPages_unregister(aPage) {
     let index = this._pages.indexOf(aPage);
@@ -234,16 +222,24 @@ let AllPages = {
    * Enables or disables the 'New Tab Page' feature.
    */
   set enabled(aEnabled) {
     if (this.enabled != aEnabled)
       Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, !!aEnabled);
   },
 
   /**
+   * Returns the number of registered New Tab Pages (i.e. the number of open
+   * about:newtab instances).
+   */
+  get length() {
+    return this._pages.length;
+  },
+
+  /**
    * Updates all currently active pages but the given one.
    * @param aExceptPage The page to exclude from updating.
    */
   update: function AllPages_update(aExceptPage) {
     this._pages.forEach(function (aPage) {
       if (aExceptPage != aPage)
         aPage.update();
     });
@@ -259,16 +255,25 @@ let AllPages = {
 
     let args = Array.slice(arguments);
 
     this._pages.forEach(function (aPage) {
       aPage.observe.apply(aPage, args);
     }, this);
   },
 
+  /**
+   * Adds a preference observer and turns itself into a no-op after the first
+   * invokation.
+   */
+  _addObserver: function AllPages_addObserver() {
+    Services.prefs.addObserver(PREF_NEWTAB_ENABLED, this, true);
+    this._addObserver = function () {};
+  },
+
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference])
 };
 
 /**
  * Singleton that keeps track of all pinned links and their positions in the
  * grid.
  */
@@ -507,16 +512,18 @@ let Links = {
 
     if (this._links && !aForce) {
       executeCallbacks();
     } else {
       this._provider.getLinks(function (aLinks) {
         this._links = aLinks;
         executeCallbacks();
       }.bind(this));
+
+      this._addObserver();
     }
   },
 
   /**
    * Gets the current set of links contained in the grid.
    * @return The links in the grid.
    */
   getLinks: function Links_getLinks() {
@@ -539,17 +546,42 @@ let Links = {
     return pinnedLinks;
   },
 
   /**
    * Resets the links cache.
    */
   resetCache: function Links_resetCache() {
     this._links = [];
-  }
+  },
+
+  /**
+   * Implements the nsIObserver interface to get notified about browser history
+   * sanitization.
+   */
+  observe: function Links_observe(aSubject, aTopic, aData) {
+    // Make sure to update open about:newtab instances. If there are no opened
+    // pages we can just wait for the next new tab to populate the cache again.
+    if (AllPages.length && AllPages.enabled)
+      this.populateCache(function () { AllPages.update() }, true);
+    else
+      this._links = null;
+  },
+
+  /**
+   * Adds a sanitization observer and turns itself into a no-op after the first
+   * invokation.
+   */
+  _addObserver: function Links_addObserver() {
+    Services.obs.addObserver(this, "browser:purge-session-history", true);
+    this._addObserver = function () {};
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference])
 };
 
 /**
  * Singleton that provides the public API of this JSM.
  */
 let NewTabUtils = {
   /**
    * Resets the NewTabUtils module, its links and its storage.