Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-12-01 12:30:36 +01:00
commit 5ec6bf8b88
23 changed files with 284 additions and 114 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "e76a327f520999e98945285d76f81b5e6dc2b69a",
"revision": "7002469f466257afd358ed9113886ef94a7fa459",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="82926b52885b1a29da893764c2cc03f938a3b0cd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="39214fb22c203e8849aaa1c27b773eeb73212921"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -24,48 +24,53 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
* the panel to the button which triggers it.
* @param {String} [tabId] Identifier of the tab to select when the panel is
* opened. Example: 'rooms', 'contacts', etc.
* @return {Promise}
*/
openCallPanel: function(event, tabId = null) {
let callback = iframe => {
// Helper function to show a specific tab view in the panel.
function showTab() {
if (!tabId) {
return new Promise((resolve) => {
let callback = iframe => {
// Helper function to show a specific tab view in the panel.
function showTab() {
if (!tabId) {
resolve();
return;
}
let win = iframe.contentWindow;
let ev = new win.CustomEvent("UIAction", Cu.cloneInto({
detail: {
action: "selectTab",
tab: tabId
}
}, win));
win.dispatchEvent(ev);
resolve();
}
// If the panel has been opened and initialized before, we can skip waiting
// for the content to load - because it's already there.
if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") {
showTab();
return;
}
let win = iframe.contentWindow;
let ev = new win.CustomEvent("UIAction", Cu.cloneInto({
detail: {
action: "selectTab",
tab: tabId
}
}, win));
win.dispatchEvent(ev);
}
iframe.addEventListener("DOMContentLoaded", function documentDOMLoaded() {
iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
injectLoopAPI(iframe.contentWindow);
iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() {
iframe.contentWindow.removeEventListener("loopPanelInitialized",
loopPanelInitialized);
showTab();
});
}, true);
};
// If the panel has been opened and initialized before, we can skip waiting
// for the content to load - because it's already there.
if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") {
showTab();
return;
}
// Used to clear the temporary "login" state from the button.
Services.obs.notifyObservers(null, "loop-status-changed", null);
iframe.addEventListener("DOMContentLoaded", function documentDOMLoaded() {
iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
injectLoopAPI(iframe.contentWindow);
iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() {
iframe.contentWindow.removeEventListener("loopPanelInitialized",
loopPanelInitialized);
showTab();
});
}, true);
};
// Used to clear the temporary "login" state from the button.
Services.obs.notifyObservers(null, "loop-status-changed", null);
PanelFrame.showPopup(window, event ? event.target : this.toolbarButton.node,
"loop", null, "about:looppanel", null, callback);
PanelFrame.showPopup(window, event ? event.target : this.toolbarButton.node,
"loop", null, "about:looppanel", null, callback);
});
},
/**

View File

@ -64,7 +64,7 @@ function promiseGetMozLoopAPI() {
let frameId = btn.getAttribute("notificationFrameId");
let frame = document.getElementById(frameId);
if (frame) {
loopPanel.removeChild(frame);
frame.remove();
}
});
});

View File

@ -111,15 +111,15 @@ this.UITour = {
},
widgetName: "PanelUI-customize",
}],
["devtools", {query: "#developer-button"}],
["help", {query: "#PanelUI-help"}],
["home", {query: "#home-button"}],
["loop", {query: "#loop-button"}],
["devtools", {query: "#developer-button"}],
["webide", {query: "#webide-button"}],
["forget", {
query: "#panic-button",
widgetName: "panic-button",
allowAdd: true }],
allowAdd: true,
}],
["loop", {query: "#loop-button"}],
["privateWindow", {query: "#privatebrowsing-button"}],
["quit", {query: "#PanelUI-quit"}],
["search", {
@ -188,6 +188,7 @@ this.UITour = {
query: "#urlbar",
widgetName: "urlbar-container",
}],
["webide", {query: "#webide-button"}],
]),
init: function() {
@ -714,8 +715,6 @@ this.UITour = {
teardownTour: function(aWindow, aWindowClosing = false) {
log.debug("teardownTour: aWindowClosing = " + aWindowClosing);
aWindow.gBrowser.tabContainer.removeEventListener("TabSelect", this);
aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hidePanelAnnotations);
aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hidePanelAnnotations);
aWindow.removeEventListener("SSWindowClosing", this);
let originTabs = this.originTabs.get(aWindow);
@ -732,8 +731,15 @@ this.UITour = {
this.hideInfo(aWindow);
// Ensure the menu panel is hidden before calling recreatePopup so popup events occur.
this.hideMenu(aWindow, "appMenu");
this.hideMenu(aWindow, "loop");
}
// Clean up panel listeners after we may have called hideMenu above.
aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hidePanelAnnotations);
aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hidePanelAnnotations);
let loopPanel = aWindow.document.getElementById("loop-notification-panel");
loopPanel.removeEventListener("popuphidden", this.onLoopPanelHidden);
this.endUrlbarCapture(aWindow);
this.removePinnedTab(aWindow);
this.resetTheme();
@ -867,7 +873,7 @@ this.UITour = {
*/
_setAppMenuStateForAnnotation: function(aWindow, aAnnotationType, aShouldOpenForHighlight, aCallback = null) {
log.debug("_setAppMenuStateForAnnotation:", aAnnotationType);
log.debug("_setAppMenuStateForAnnotation: Menu is exptected to be:", aShouldOpenForHighlight ? "open" : "closed");
log.debug("_setAppMenuStateForAnnotation: Menu is expected to be:", aShouldOpenForHighlight ? "open" : "closed");
// If the panel is in the desired state, we're done.
let panelIsOpen = aWindow.PanelUI.panel.state != "closed";
@ -1238,6 +1244,26 @@ this.UITour = {
} else if (aMenuName == "bookmarks") {
let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
openMenuButton(menuBtn);
} else if (aMenuName == "loop") {
let toolbarButton = aWindow.LoopUI.toolbarButton;
if (!toolbarButton || !toolbarButton.node) {
return;
}
let panel = aWindow.document.getElementById("loop-notification-panel");
panel.setAttribute("noautohide", true);
if (panel.state != "open") {
this.recreatePopup(panel);
}
// An event object is expected but we don't want to toggle the panel with a click if the panel
// is already open.
aWindow.LoopUI.openCallPanel({ target: toolbarButton.node, }).then(() => {
if (aOpenCallback) {
aOpenCallback();
}
});
panel.addEventListener("popuphidden", this.onLoopPanelHidden);
} else if (aMenuName == "searchEngines") {
this.getTarget(aWindow, "searchProvider").then(target => {
openMenuButton(target.node);
@ -1258,6 +1284,9 @@ this.UITour = {
} else if (aMenuName == "bookmarks") {
let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
closeMenuButton(menuBtn);
} else if (aMenuName == "loop") {
let panel = aWindow.document.getElementById("loop-notification-panel");
panel.hidePopup();
} else if (aMenuName == "searchEngines") {
let menuBtn = this.targets.get("searchProvider").query(aWindow.document);
closeMenuButton(menuBtn);
@ -1289,6 +1318,11 @@ this.UITour = {
UITour.appMenuOpenForAnnotation.clear();
},
onLoopPanelHidden: function(aEvent) {
aEvent.target.removeAttribute("noautohide");
UITour.recreatePopup(aEvent.target);
},
recreatePopup: function(aPanel) {
// After changing popup attributes that relate to how the native widget is created
// (e.g. @noautohide) we need to re-create the frame/widget for it to take effect.

View File

@ -28,6 +28,8 @@ skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
[browser_UITour_annotation_size_attributes.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
[browser_UITour_loop.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
[browser_UITour_modalDialog.js]
run-if = os == "mac" # modal dialog disabling only working on OS X
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly

View File

@ -89,7 +89,7 @@ let tests = [
yield addPinnedTabPromise();
is(gBrowser.tabs[1].pinned, false, "After multiple calls of addPinnedTab, should still only have one pinned tab");
}),
taskify(function* test_menu() {
taskify(function* test_bookmarks_menu() {
let bookmarksMenuButton = document.getElementById("bookmarks-menu-button");
ise(bookmarksMenuButton.open, false, "Menu should initially be closed");

View File

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gTestTab;
let gContentAPI;
let gContentWindow;
let loopButton;
let loopPanel = document.getElementById("loop-notification-panel");
Components.utils.import("resource:///modules/UITour.jsm");
function test() {
UITourTest();
}
let tests = [
taskify(function* test_menu_show_hide() {
ise(loopButton.open, false, "Menu should initially be closed");
gContentAPI.showMenu("loop");
yield waitForConditionPromise(() => {
return loopButton.open;
}, "Menu should be visible after showMenu()");
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
is(loopPanel.state, "open", "The panel should be open");
ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open");
gContentAPI.hideMenu("loop");
yield waitForConditionPromise(() => {
return !loopButton.open;
}, "Menu should be hidden after hideMenu()");
checkLoopPanelIsHidden();
}),
// Test the menu was cleaned up in teardown.
taskify(function* setup_menu_cleanup() {
gContentAPI.showMenu("loop");
yield waitForConditionPromise(() => {
return loopButton.open;
}, "Menu should be visible after showMenu()");
// Leave it open so it gets torn down and we can test below that teardown was succesful.
}),
taskify(function* test_menu_cleanup() {
// Test that the open menu from above was torn down fully.
checkLoopPanelIsHidden();
}),
];
function checkLoopPanelIsHidden() {
ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
isnot(loopPanel.state, "open", "The panel shouldn't be open");
is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
}
if (Services.prefs.getBoolPref("loop.enabled")) {
loopButton = window.LoopUI.toolbarButton.node;
registerCleanupFunction(() => {
// Copied from browser/components/loop/test/mochitest/head.js
// Remove the iframe after each test. This also avoids mochitest complaining
// about leaks on shutdown as we intentionally hold the iframe open for the
// life of the application.
let frameId = loopButton.getAttribute("notificationFrameId");
let frame = document.getElementById(frameId);
if (frame) {
frame.remove();
}
});
} else {
ok(true, "Loop is disabled so skip the UITour Loop tests");
tests = [];
}

View File

@ -34,7 +34,7 @@ treecol {
}
#category-search > .category-icon {
-moz-image-region: rect(0, 194px, 24px, 168px);
-moz-image-region: rect(0, 192px, 24px, 168px);
}
#category-content > .category-icon {

View File

@ -747,6 +747,7 @@ browser.jar:
skin/classic/aero/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
* skin/classic/aero/browser/devtools/netmonitor.css (devtools/netmonitor.css)
* skin/classic/aero/browser/devtools/profiler.css (devtools/profiler.css)
* skin/classic/aero/browser/devtools/performance.css (devtools/performance.css)
* skin/classic/aero/browser/devtools/timeline.css (devtools/timeline.css)
* skin/classic/aero/browser/devtools/scratchpad.css (devtools/scratchpad.css)
* skin/classic/aero/browser/devtools/shadereditor.css (devtools/shadereditor.css)

View File

@ -2,13 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
this.EXPORTED_SYMBOLS = ['TabEngine', 'TabSetRecord'];
this.EXPORTED_SYMBOLS = ["TabEngine", "TabSetRecord"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
const TABS_TTL = 604800; // 7 days
const TABS_TTL = 604800; // 7 days.
const TAB_ENTRIES_LIMIT = 25; // How many URLs to include in tab history.
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -27,7 +26,7 @@ this.TabSetRecord = function TabSetRecord(collection, id) {
TabSetRecord.prototype = {
__proto__: CryptoWrapper.prototype,
_logName: "Sync.Record.Tabs",
ttl: TABS_TTL
ttl: TABS_TTL,
};
Utils.deferGetSet(TabSetRecord, "cleartext", ["clientName", "tabs"]);
@ -36,7 +35,7 @@ Utils.deferGetSet(TabSetRecord, "cleartext", ["clientName", "tabs"]);
this.TabEngine = function TabEngine(service) {
SyncEngine.call(this, "Tabs", service);
// Reset the client on every startup so that we fetch recent tabs
// Reset the client on every startup so that we fetch recent tabs.
this._resetClient();
}
TabEngine.prototype = {
@ -47,7 +46,7 @@ TabEngine.prototype = {
syncPriority: 3,
getChangedIDs: function getChangedIDs() {
getChangedIDs: function () {
// No need for a proper timestamp (no conflict resolution needed).
let changedIDs = {};
if (this._tracker.modified)
@ -55,22 +54,22 @@ TabEngine.prototype = {
return changedIDs;
},
// API for use by Weave UI code to give user choices of tabs to open:
getAllClients: function TabEngine_getAllClients() {
// API for use by Sync UI code to give user choices of tabs to open.
getAllClients: function () {
return this._store._remoteClients;
},
getClientById: function TabEngine_getClientById(id) {
getClientById: function (id) {
return this._store._remoteClients[id];
},
_resetClient: function TabEngine__resetClient() {
_resetClient: function () {
SyncEngine.prototype._resetClient.call(this);
this._store.wipe();
this._tracker.modified = true;
},
removeClientData: function removeClientData() {
removeClientData: function () {
let url = this.engineURL + "/" + this.service.clientsEngine.localID;
this.service.resource(url).delete();
},
@ -94,7 +93,7 @@ function TabStore(name, engine) {
TabStore.prototype = {
__proto__: Store.prototype,
itemExists: function TabStore_itemExists(id) {
itemExists: function (id) {
return id == this.engine.service.clientsEngine.localID;
},
@ -131,23 +130,40 @@ TabStore.prototype = {
continue;
}
// Until we store full or partial history, just grab the current entry.
// index is 1 based, so make sure we adjust.
let entry = tabState.entries[tabState.index - 1];
let acceptable = !filter ? (url) => url :
(url) => url && !filteredUrls.test(url);
// Filter out some urls if necessary. SessionStore can return empty
// tabs in some cases - easiest thing is to just ignore them for now.
if (!entry.url || filter && filteredUrls.test(entry.url)) {
let entries = tabState.entries;
let index = tabState.index;
let current = entries[index - 1];
// We ignore the tab completely if the current entry url is
// not acceptable (we need something accurate to open).
if (!acceptable(current.url)) {
continue;
}
// I think it's also possible that attributes[.image] might not be set
// so handle that as well.
// The element at `index` is the current page. Previous URLs were
// previously visited URLs; subsequent URLs are in the 'forward' stack,
// which we can't represent in Sync, so we truncate here.
let candidates = (entries.length == index) ?
entries :
entries.slice(0, index);
let urls = candidates.map((entry) => entry.url)
.filter(acceptable)
.reverse(); // Because Sync puts current at index 0, and history after.
// Truncate if necessary.
if (urls.length > TAB_ENTRIES_LIMIT) {
urls.length = TAB_ENTRIES_LIMIT;
}
allTabs.push({
title: entry.title || "",
urlHistory: [entry.url],
title: current.title || "",
urlHistory: urls,
icon: tabState.attributes && tabState.attributes.image || "",
lastUsed: Math.floor((tabState.lastAccessed || 0) / 1000)
lastUsed: Math.floor((tabState.lastAccessed || 0) / 1000),
});
}
}
@ -155,7 +171,7 @@ TabStore.prototype = {
return allTabs;
},
createRecord: function createRecord(id, collection) {
createRecord: function (id, collection) {
let record = new TabSetRecord(collection, id);
record.clientName = this.engine.service.clientsEngine.localName;
@ -188,7 +204,7 @@ TabStore.prototype = {
return record;
},
getAllIDs: function TabStore_getAllIds() {
getAllIDs: function () {
// Don't report any tabs if all windows are in private browsing for
// first syncs.
let ids = {};
@ -214,31 +230,38 @@ TabStore.prototype = {
return ids;
},
wipe: function TabStore_wipe() {
wipe: function () {
this._remoteClients = {};
},
create: function TabStore_create(record) {
create: function (record) {
this._log.debug("Adding remote tabs from " + record.clientName);
this._remoteClients[record.id] = record.cleartext;
// Lose some precision, but that's good enough (seconds)
// Lose some precision, but that's good enough (seconds).
let roundModify = Math.floor(record.modified / 1000);
let notifyState = Svc.Prefs.get("notifyTabState");
// If there's no existing pref, save this first modified time
if (notifyState == null)
// If there's no existing pref, save this first modified time.
if (notifyState == null) {
Svc.Prefs.set("notifyTabState", roundModify);
// Don't change notifyState if it's already 0 (don't notify)
else if (notifyState == 0)
return;
// We must have gotten a new tab that isn't the same as last time
else if (notifyState != roundModify)
}
// Don't change notifyState if it's already 0 (don't notify).
if (notifyState == 0) {
return;
}
// We must have gotten a new tab that isn't the same as last time.
if (notifyState != roundModify) {
Svc.Prefs.set("notifyTabState", 0);
}
},
update: function update(record) {
update: function (record) {
this._log.trace("Ignoring tab updates as local ones win");
}
},
};
@ -247,7 +270,7 @@ function TabTracker(name, engine) {
Svc.Obs.add("weave:engine:start-tracking", this);
Svc.Obs.add("weave:engine:stop-tracking", this);
// Make sure "this" pointer is always set correctly for event listeners
// Make sure "this" pointer is always set correctly for event listeners.
this.onTab = Utils.bind2(this, this.onTab);
this._unregisterListeners = Utils.bind2(this, this._unregisterListeners);
}
@ -256,16 +279,17 @@ TabTracker.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
loadChangedIDs: function loadChangedIDs() {
loadChangedIDs: function () {
// Don't read changed IDs from disk at start up.
},
clearChangedIDs: function clearChangedIDs() {
clearChangedIDs: function () {
this.modified = false;
},
_topics: ["pageshow", "TabOpen", "TabClose", "TabSelect"],
_registerListenersForWindow: function registerListenersFW(window) {
_registerListenersForWindow: function (window) {
this._log.trace("Registering tab listeners in window");
for each (let topic in this._topics) {
window.addEventListener(topic, this.onTab, false);
@ -273,11 +297,11 @@ TabTracker.prototype = {
window.addEventListener("unload", this._unregisterListeners, false);
},
_unregisterListeners: function unregisterListeners(event) {
_unregisterListeners: function (event) {
this._unregisterListenersForWindow(event.target);
},
_unregisterListenersForWindow: function unregisterListenersFW(window) {
_unregisterListenersForWindow: function (window) {
this._log.trace("Removing tab listeners in window");
window.removeEventListener("unload", this._unregisterListeners, false);
for each (let topic in this._topics) {
@ -318,7 +342,7 @@ TabTracker.prototype = {
}
},
onTab: function onTab(event) {
onTab: function (event) {
if (event.originalTarget.linkedBrowser) {
let browser = event.originalTarget.linkedBrowser;
if (PrivateBrowsingUtils.isBrowserPrivate(browser) &&
@ -334,7 +358,8 @@ TabTracker.prototype = {
// For page shows, bump the score 10% of the time, emulating a partial
// score. We don't want to sync too frequently. For all other page
// events, always bump the score.
if (event.type != "pageshow" || Math.random() < .1)
if (event.type != "pageshow" || Math.random() < .1) {
this.score += SCORE_INCREMENT_SMALL;
}
},
}
};

View File

@ -138,8 +138,16 @@ function mockGetTabState (tab) {
return tab;
}
function mockGetWindowEnumerator(url, numWindows, numTabs) {
function mockGetWindowEnumerator(url, numWindows, numTabs, indexes, moreURLs) {
let elements = [];
function url2entry(url) {
return {
url: ((typeof url == "function") ? url() : url),
title: "title"
};
}
for (let w = 0; w < numWindows; ++w) {
let tabs = [];
let win = {
@ -153,11 +161,8 @@ function mockGetWindowEnumerator(url, numWindows, numTabs) {
for (let t = 0; t < numTabs; ++t) {
tabs.push(TestingUtils.deepCopy({
index: 1,
entries: [{
url: ((typeof url == "string") ? url : url()),
title: "title"
}],
index: indexes ? indexes() : 1,
entries: (moreURLs ? [url].concat(moreURLs()) : [url]).map(url2entry),
attributes: {
image: "image"
},

View File

@ -52,23 +52,43 @@ function test_getAllTabs() {
let store = getMockStore();
let tabs;
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://foo.com", 1, 1);
let threeUrls = ["http://foo.com", "http://fuubar.com", "http://barbar.com"];
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://bar.com", 1, 1, () => 2, () => threeUrls);
_("Get all tabs.");
tabs = store.getAllTabs();
_("Tabs: " + JSON.stringify(tabs));
do_check_eq(tabs.length, 1);
do_check_eq(tabs[0].title, "title");
do_check_eq(tabs[0].urlHistory.length, 1);
do_check_eq(tabs[0].urlHistory[0], ["http://foo.com"]);
do_check_eq(tabs[0].urlHistory.length, 2);
do_check_eq(tabs[0].urlHistory[0], "http://foo.com");
do_check_eq(tabs[0].urlHistory[1], "http://bar.com");
do_check_eq(tabs[0].icon, "image");
do_check_eq(tabs[0].lastUsed, 1);
_("Get all tabs, and check that filtering works.");
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "about:foo", 1, 1);
let twoUrls = ["about:foo", "http://fuubar.com"];
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://foo.com", 1, 1, () => 2, () => twoUrls);
tabs = store.getAllTabs(true);
_("Filtered: " + JSON.stringify(tabs));
do_check_eq(tabs.length, 0);
_("Get all tabs, and check that the entries safety limit works.");
let allURLs = [];
for (let i = 0; i < 50; i++) {
allURLs.push("http://foo" + i + ".bar");
}
allURLs.splice(35, 0, "about:foo", "about:bar", "about:foobar");
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://bar.com", 1, 1, () => 45, () => allURLs);
tabs = store.getAllTabs((url) => url.startsWith("about"));
_("Sliced: " + JSON.stringify(tabs));
do_check_eq(tabs.length, 1);
do_check_eq(tabs[0].urlHistory.length, 25);
do_check_eq(tabs[0].urlHistory[0], "http://foo40.bar");
do_check_eq(tabs[0].urlHistory[24], "http://foo16.bar");
}
function test_createRecord() {