merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-07-08 15:10:56 +02:00
commit a98c31f1a8
121 changed files with 1944 additions and 831 deletions

View File

@ -0,0 +1,25 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
const globalMM = Components.classes["@mozilla.org/globalmessagemanager;1"].
getService(Components.interfaces.nsIMessageListenerManager);
// Load frame scripts from the same dir as this module.
// Since this JSM will be loaded using require(), PATH will be
// overridden while running tests, just like any other module.
const PATH = __URI__.replace('FrameScriptManager.jsm', '');
// ensure frame scripts are loaded only once
let loadedTabEvents = false;
function enableTabEvents() {
if (loadedTabEvents)
return;
loadedTabEvents = true;
globalMM.loadFrameScript(PATH + 'tab-events.js', true);
}
const EXPORTED_SYMBOLS = ['enableTabEvents'];

View File

@ -0,0 +1,44 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
// bug 673569 - let each frame script have its own anonymous scope
(function() {
const observerSvc = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
// map observer topics to tab event names
const EVENTS = {
'content-document-interactive': 'ready',
'chrome-document-interactive': 'ready',
'content-document-loaded': 'load',
'chrome-document-loaded': 'load',
// 'content-page-shown': 'pageshow', // bug 1024105
}
let listener = {
observe: function(subject, topic) {
// observer service keeps a strong reference to the listener, and this
// method can get called after the tab is closed, so we should remove it.
if (!docShell) {
observerSvc.removeObserver(this, topic);
}
else {
if (subject === content.document)
sendAsyncMessage('sdk/tab/event', { type: EVENTS[topic] });
}
}
}
Object.keys(EVENTS).forEach( (topic) =>
observerSvc.addObserver(listener, topic, false));
// bug 1024105 - content-page-shown notification doesn't pass persisted param
docShell.chromeEventHandler.addEventListener('pageshow', (e) => {
if (e.target === content.document)
sendAsyncMessage('sdk/tab/event', { type: e.type, persisted: e.persisted });
}, true);
})();

View File

@ -199,6 +199,7 @@ destroy.define(Worker, function (worker, reason) {
// Specifying no type or listener removes all listeners
// from target
off(worker);
off(worker.port);
});
/**

View File

@ -10,8 +10,9 @@ module.metadata = {
const { Cc, Ci, Cr } = require("chrome");
const { emit, on, off } = require("./core");
const { addObserver } = Cc['@mozilla.org/observer-service;1'].
const { addObserver, removeObserver } = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
const { when: unload } = require("../system/unload");
// Simple class that can be used to instantiate event channel that
// implements `nsIObserver` interface. It's will is used by `observe`
@ -48,6 +49,11 @@ function observe(topic) {
// will be held.
addObserver(observerChannel, topic, true);
// We need to remove any observer added once the add-on is unloaded;
// otherwise we'll get a "dead object" exception.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1001833
unload(() => removeObserver(observerChannel, topic));
return observerChannel;
}

View File

@ -8,7 +8,21 @@ module.metadata = {
"stability": "unstable"
};
const { Ci } = require("chrome");
let { emit } = require("./core");
let { when: unload } = require("../system/unload");
let listeners = new Map();
let getWindowFrom = x =>
x instanceof Ci.nsIDOMWindow ? x :
x instanceof Ci.nsIDOMDocument ? x.defaultView :
x instanceof Ci.nsIDOMNode ? x.ownerDocument.defaultView :
null;
function removeFromListeners() {
listeners.delete(this);
}
// Simple utility function takes event target, event type and optional
// `options.capture` and returns node style event stream that emits "data"
@ -16,11 +30,41 @@ let { emit } = require("./core");
function open(target, type, options) {
let output = {};
let capture = options && options.capture ? true : false;
let listener = (event) => emit(output, "data", event);
target.addEventListener(type, function(event) {
emit(output, "data", event);
}, capture);
// `open` is currently used only on DOM Window objects, however it was made
// to be used to any kind of `target` that supports `addEventListener`,
// therefore is safer get the `window` from the `target` instead assuming
// that `target` is the `window`.
let window = getWindowFrom(target);
// If we're not able to get a `window` from `target`, there is something
// wrong. We cannot add listeners that can leak later, or results in
// "dead object" exception.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1001833
if (!window)
throw new Error("Unable to obtain the owner window from the target given.");
let cleaners = listeners.get(window) || [];
cleaners.push(() => target.removeEventListener(type, listener, capture));
listeners.set(window, cleaners);
// We need to remove from our map the `window` once is closed, to prevent
// memory leak
window.addEventListener("DOMWindowClose", removeFromListeners);
target.addEventListener(type, listener, capture);
return output;
}
unload(() => {
for (let [window, cleaners] of listeners) {
cleaners.forEach(callback => callback())
}
listeners.clear();
});
exports.open = open;

View File

@ -17,6 +17,7 @@ function Options(options) {
},
isPinned: { is: ["undefined", "boolean"] },
isPrivate: { is: ["undefined", "boolean"] },
inNewWindow: { is: ["undefined", "boolean"] },
onOpen: { is: ["undefined", "function"] },
onClose: { is: ["undefined", "function"] },
onReady: { is: ["undefined", "function"] },

View File

@ -21,6 +21,10 @@ const { getURL } = require('../url/utils');
const { viewFor } = require('../view/core');
const { observer } = require('./observer');
// cfx doesn't know require() now handles JSM modules
const FRAMESCRIPT_MANAGER = '../../framescript/FrameScriptManager.jsm';
require(FRAMESCRIPT_MANAGER).enableTabEvents();
// Array of the inner instances of all the wrapped tabs.
const TABS = [];
@ -39,9 +43,6 @@ const TabTrait = Trait.compose(EventEmitter, {
*/
window: null,
constructor: function Tab(options) {
this._onReady = this._onReady.bind(this);
this._onLoad = this._onLoad.bind(this);
this._onPageShow = this._onPageShow.bind(this);
this._tab = options.tab;
// TODO: Remove this dependency
let window = this.window = options.window || require('../windows').BrowserWindow({ window: getOwnerWindow(this._tab) });
@ -59,9 +60,12 @@ const TabTrait = Trait.compose(EventEmitter, {
this.on(EVENTS.close.name, this.destroy.bind(this));
this._browser.addEventListener(EVENTS.ready.dom, this._onReady, true);
this._browser.addEventListener(EVENTS.load.dom, this._onLoad, true);
this._browser.addEventListener(EVENTS.pageshow.dom, this._onPageShow, true);
this._onContentEvent = this._onContentEvent.bind(this);
this._browser.messageManager.addMessageListener('sdk/tab/event', this._onContentEvent);
// bug 1024632 - first tab inNewWindow gets events from the synthetic
// about:blank document. ignore them unless that is the actual target url.
this._skipBlankEvents = options.inNewWindow && options.url !== 'about:blank';
if (options.isPinned)
this.pin();
@ -84,9 +88,7 @@ const TabTrait = Trait.compose(EventEmitter, {
let browser = this._browser;
// The tab may already be removed from DOM -or- not yet added
if (browser) {
browser.removeEventListener(EVENTS.ready.dom, this._onReady, true);
browser.removeEventListener(EVENTS.load.dom, this._onLoad, true);
browser.removeEventListener(EVENTS.pageshow.dom, this._onPageShow, true);
browser.messageManager.removeMessageListener('sdk/tab/event', this._onContentEvent);
}
this._tab = null;
TABS.splice(TABS.indexOf(this), 1);
@ -94,36 +96,20 @@ const TabTrait = Trait.compose(EventEmitter, {
},
/**
* Internal listener that emits public event 'ready' when the page of this
* tab is loaded, from DOMContentLoaded
* internal message listener emits public events (ready, load and pageshow)
* forwarded from content frame script tab-event.js
*/
_onReady: function _onReady(event) {
// IFrames events will bubble so we need to ignore those.
if (event.target == this._contentDocument)
this._emit(EVENTS.ready.name, this._public);
_onContentEvent: function({ data }) {
// bug 1024632 - skip initial events from synthetic about:blank document
if (this._skipBlankEvents && this.window.tabs.length === 1 && this.url === 'about:blank')
return;
// first time we don't skip blank events, disable further skipping
this._skipBlankEvents = false;
this._emit(data.type, this._public, data.persisted);
},
/**
* Internal listener that emits public event 'load' when the page of this
* tab is loaded, for triggering on non-HTML content, bug #671305
*/
_onLoad: function _onLoad(event) {
// IFrames events will bubble so we need to ignore those.
if (event.target == this._contentDocument) {
this._emit(EVENTS.load.name, this._public);
}
},
/**
* Internal listener that emits public event 'pageshow' when the page of this
* tab is loaded from cache, bug #671305
*/
_onPageShow: function _onPageShow(event) {
// IFrames events will bubble so we need to ignore those.
if (event.target == this._contentDocument) {
this._emit(EVENTS.pageshow.name, this._public, event.persisted);
}
},
/**
* Internal tab event router. Window will emit tab related events for all it's
* tabs, this listener will propagate all the events for this tab to it's

View File

@ -33,6 +33,7 @@ const { isLocalURL } = require('../url');
const { ensure } = require('../system/unload');
const { identify } = require('./id');
const { uuid } = require('../util/uuid');
const { viewFor } = require('../view/core');
const sidebarNS = ns();
@ -239,12 +240,8 @@ const Sidebar = Class({
updateURL(this, v);
modelFor(this).url = v;
},
show: function() {
return showSidebar(null, this);
},
hide: function() {
return hideSidebar(null, this);
},
show: function(window) showSidebar(viewFor(window), this),
hide: function(window) hideSidebar(viewFor(window), this),
dispose: function() {
const internals = sidebarNS(this);

View File

@ -70,6 +70,9 @@ const BrowserWindowTrait = Trait.compose(
else if ('url' in options) {
this._tabOptions = [ Options(options.url) ];
}
for (let tab of this._tabOptions) {
tab.inNewWindow = true;
}
this._isPrivate = isPrivateBrowsingSupported && !!options.isPrivate;

View File

@ -0,0 +1,13 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
'use strict';
const { merge } = require('sdk/util/object');
merge(module.exports, require('./test-tab'));
merge(module.exports, require('./test-tab-events'));
merge(module.exports, require('./test-tab-observer'));
merge(module.exports, require('./test-tab-utils'));
require('sdk/test/runner').runTestsFromModule(module);

View File

@ -0,0 +1,100 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
'use strict';
const { Loader } = require('sdk/test/loader');
const { loader } = LoaderWithHookedConsole(module);
const pb = loader.require('sdk/private-browsing');
const pbUtils = loader.require('sdk/private-browsing/utils');
const xulApp = require("sdk/system/xul-app");
const { open: openWindow, getMostRecentBrowserWindow } = require('sdk/window/utils');
const { openTab, getTabContentWindow, getActiveTab, setTabURL, closeTab } = require('sdk/tabs/utils');
const promise = require("sdk/core/promise");
const windowHelpers = require('sdk/window/helpers');
const events = require("sdk/system/events");
function LoaderWithHookedConsole(module) {
let globals = {};
let errors = [];
globals.console = Object.create(console, {
error: {
value: function(e) {
errors.push(e);
if (!/DEPRECATED:/.test(e)) {
console.error(e);
}
}
}
});
let loader = Loader(module, globals);
return {
loader: loader,
errors: errors
}
}
function deactivate(callback) {
if (pbUtils.isGlobalPBSupported) {
if (callback)
pb.once('stop', callback);
pb.deactivate();
}
}
exports.deactivate = deactivate;
exports.pb = pb;
exports.pbUtils = pbUtils;
exports.LoaderWithHookedConsole = LoaderWithHookedConsole;
exports.openWebpage = function openWebpage(url, enablePrivate) {
if (xulApp.is("Fennec")) {
let chromeWindow = getMostRecentBrowserWindow();
let rawTab = openTab(chromeWindow, url, {
isPrivate: enablePrivate
});
return {
ready: promise.resolve(getTabContentWindow(rawTab)),
close: function () {
closeTab(rawTab);
// Returns a resolved promise as there is no need to wait
return promise.resolve();
}
};
}
else {
let win = openWindow(null, {
features: {
private: enablePrivate
}
});
let deferred = promise.defer();
// Wait for delayed startup code to be executed, in order to ensure
// that the window is really ready
events.on("browser-delayed-startup-finished", function onReady({subject}) {
if (subject == win) {
events.off("browser-delayed-startup-finished", onReady);
deferred.resolve(win);
let rawTab = getActiveTab(win);
setTabURL(rawTab, url);
deferred.resolve(getTabContentWindow(rawTab));
}
}, true);
return {
ready: deferred.promise,
close: function () {
return windowHelpers.close(win);
}
};
}
return null;
}

View File

@ -0,0 +1,238 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
const { Loader } = require("sdk/test/loader");
const utils = require("sdk/tabs/utils");
const { open, close } = require("sdk/window/helpers");
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const { events } = require("sdk/tab/events");
const { on, off } = require("sdk/event/core");
const { resolve, defer } = require("sdk/core/promise");
let isFennec = require("sdk/system/xul-app").is("Fennec");
function test(options) {
return function(assert, done) {
let tabEvents = [];
let tabs = [];
let { promise, resolve: resolveP } = defer();
let win = isFennec ? resolve(getMostRecentBrowserWindow()) :
open(null, {
features: { private: true, toolbar:true, chrome: true }
});
let window = null;
// Firefox events are fired sync; Fennec events async
// this normalizes the tests
function handler (event) {
tabEvents.push(event);
runIfReady();
}
function runIfReady () {
let releventEvents = getRelatedEvents(tabEvents, tabs);
if (options.readyWhen(releventEvents))
options.end({
tabs: tabs,
events: releventEvents,
assert: assert,
done: resolveP
});
}
win.then(function(w) {
window = w;
on(events, "data", handler);
options.start({ tabs: tabs, window: window });
// Execute here for synchronous FF events, as the handlers
// were called before tabs were pushed to `tabs`
runIfReady();
return promise;
}).then(function() {
off(events, "data", handler);
return isFennec ? null : close(window);
}).then(done, assert.fail);
};
}
// Just making sure that tab events work for already opened tabs not only
// for new windows.
exports["test current window"] = test({
readyWhen: events => events.length === 3,
start: ({ tabs, window }) => {
let tab = utils.openTab(window, 'data:text/plain,open');
tabs.push(tab);
utils.closeTab(tab);
},
end: ({ tabs, events, assert, done }) => {
let [open, select, close] = events;
let tab = tabs[0];
assert.equal(open.type, "TabOpen");
assert.equal(open.target, tab);
assert.equal(select.type, "TabSelect");
assert.equal(select.target, tab);
assert.equal(close.type, "TabClose");
assert.equal(close.target, tab);
done();
}
});
exports["test open"] = test({
readyWhen: events => events.length === 2,
start: ({ tabs, window }) => {
tabs.push(utils.openTab(window, 'data:text/plain,open'));
},
end: ({ tabs, events, assert, done }) => {
let [open, select] = events;
let tab = tabs[0];
assert.equal(open.type, "TabOpen");
assert.equal(open.target, tab);
assert.equal(select.type, "TabSelect");
assert.equal(select.target, tab);
done();
}
});
exports["test open -> close"] = test({
readyWhen: events => events.length === 3,
start: ({ tabs, window }) => {
// First tab is useless we just open it so that closing second tab won't
// close window on some platforms.
utils.openTab(window, 'data:text/plain,ignore');
let tab = utils.openTab(window, 'data:text/plain,open-close');
tabs.push(tab);
utils.closeTab(tab);
},
end: ({ tabs, events, assert, done }) => {
let [open, select, close] = events;
let tab = tabs[0];
assert.equal(open.type, "TabOpen");
assert.equal(open.target, tab);
assert.equal(select.type, "TabSelect");
assert.equal(select.target, tab);
assert.equal(close.type, "TabClose");
assert.equal(close.target, tab);
done();
}
});
exports["test open -> open -> select"] = test({
readyWhen: events => events.length === 5,
start: ({tabs, window}) => {
tabs.push(utils.openTab(window, 'data:text/plain,Tab-1'));
tabs.push(utils.openTab(window, 'data:text/plain,Tab-2'));
utils.activateTab(tabs[0], window);
},
end: ({ tabs, events, assert, done }) => {
let [ tab1, tab2 ] = tabs;
let tab1Events = 0;
getRelatedEvents(events, tab1).map(event => {
tab1Events++;
if (tab1Events === 1)
assert.equal(event.type, "TabOpen", "first tab opened");
else
assert.equal(event.type, "TabSelect", "first tab selected");
assert.equal(event.target, tab1);
});
assert.equal(tab1Events, 3, "first tab has 3 events");
let tab2Opened;
getRelatedEvents(events, tab2).map(event => {
if (!tab2Opened)
assert.equal(event.type, "TabOpen", "second tab opened");
else
assert.equal(event.type, "TabSelect", "second tab selected");
tab2Opened = true;
assert.equal(event.target, tab2);
});
done();
}
});
exports["test open -> pin -> unpin"] = test({
readyWhen: events => events.length === (isFennec ? 2 : 5),
start: ({ tabs, window }) => {
tabs.push(utils.openTab(window, 'data:text/plain,pin-unpin'));
utils.pin(tabs[0]);
utils.unpin(tabs[0]);
},
end: ({ tabs, events, assert, done }) => {
let [open, select, move, pin, unpin] = events;
let tab = tabs[0];
assert.equal(open.type, "TabOpen");
assert.equal(open.target, tab);
assert.equal(select.type, "TabSelect");
assert.equal(select.target, tab);
if (isFennec) {
assert.pass("Tab pin / unpin is not supported by Fennec");
}
else {
assert.equal(move.type, "TabMove");
assert.equal(move.target, tab);
assert.equal(pin.type, "TabPinned");
assert.equal(pin.target, tab);
assert.equal(unpin.type, "TabUnpinned");
assert.equal(unpin.target, tab);
}
done();
}
});
exports["test open -> open -> move "] = test({
readyWhen: events => events.length === (isFennec ? 4 : 5),
start: ({tabs, window}) => {
tabs.push(utils.openTab(window, 'data:text/plain,Tab-1'));
tabs.push(utils.openTab(window, 'data:text/plain,Tab-2'));
utils.move(tabs[0], 2);
},
end: ({ tabs, events, assert, done }) => {
let [ tab1, tab2 ] = tabs;
let tab1Events = 0;
getRelatedEvents(events, tab1).map(event => {
tab1Events++;
if (tab1Events === 1)
assert.equal(event.type, "TabOpen", "first tab opened");
else if (tab1Events === 2)
assert.equal(event.type, "TabSelect", "first tab selected");
else if (tab1Events === 3 && isFennec)
assert.equal(event.type, "TabMove", "first tab moved");
assert.equal(event.target, tab1);
});
assert.equal(tab1Events, isFennec ? 2 : 3,
"correct number of events for first tab");
let tab2Events = 0;
getRelatedEvents(events, tab2).map(event => {
tab2Events++;
if (tab2Events === 1)
assert.equal(event.type, "TabOpen", "second tab opened");
else
assert.equal(event.type, "TabSelect", "second tab selected");
assert.equal(event.target, tab2);
});
done();
}
});
function getRelatedEvents (events, tabs) {
return events.filter(({target}) => ~([].concat(tabs)).indexOf(target));
}
// require("sdk/test").run(exports);

View File

@ -0,0 +1,46 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
// TODO Fennec support in Bug #894525
module.metadata = {
"engines": {
"Firefox": "*"
}
}
const { openTab, closeTab } = require("sdk/tabs/utils");
const { Loader } = require("sdk/test/loader");
const { setTimeout } = require("sdk/timers");
exports["test unload tab observer"] = function(assert, done) {
let loader = Loader(module);
let window = loader.require("sdk/deprecated/window-utils").activeBrowserWindow;
let observer = loader.require("sdk/tabs/observer").observer;
let opened = 0;
let closed = 0;
observer.on("open", function onOpen(window) { opened++; });
observer.on("close", function onClose(window) { closed++; });
// Open and close tab to trigger observers.
closeTab(openTab(window, "data:text/html;charset=utf-8,tab-1"));
// Unload the module so that all listeners set by observer are removed.
loader.unload();
// Open and close tab once again.
closeTab(openTab(window, "data:text/html;charset=utf-8,tab-2"));
// Enqueuing asserts to make sure that assertion is not performed early.
setTimeout(function () {
assert.equal(1, opened, "observer open was called before unload only");
assert.equal(1, closed, "observer close was called before unload only");
done();
}, 0);
};
// require("test").run(exports);

View File

@ -0,0 +1,67 @@
'use strict';
const { getTabs } = require('sdk/tabs/utils');
const { isGlobalPBSupported, isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
const { browserWindows } = require('sdk/windows');
const tabs = require('sdk/tabs');
const { pb } = require('./private-browsing/helper');
const { isPrivate } = require('sdk/private-browsing');
const { openTab, closeTab, getTabContentWindow, getOwnerWindow } = require('sdk/tabs/utils');
const { open, close } = require('sdk/window/helpers');
const { windows } = require('sdk/window/utils');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { fromIterator } = require('sdk/util/array');
if (isWindowPBSupported) {
exports.testGetTabs = function(assert, done) {
let tabCount = getTabs().length;
let windowCount = browserWindows.length;
open(null, {
features: {
private: true,
toolbar: true,
chrome: true
}
}).then(function(window) {
assert.ok(isPrivate(window), 'new tab is private');
assert.equal(getTabs().length, tabCount, 'there are no new tabs found');
getTabs().forEach(function(tab) {
assert.equal(isPrivate(tab), false, 'all found tabs are not private');
assert.equal(isPrivate(getOwnerWindow(tab)), false, 'all found tabs are not private');
assert.equal(isPrivate(getTabContentWindow(tab)), false, 'all found tabs are not private');
});
assert.equal(browserWindows.length, windowCount, 'there are no new windows found');
fromIterator(browserWindows).forEach(function(window) {
assert.equal(isPrivate(window), false, 'all found windows are not private');
});
assert.equal(windows(null, {includePrivate: true}).length, 2, 'there are really two windows');
close(window).then(done);
});
};
}
else if (isTabPBSupported) {
exports.testGetTabs = function(assert, done) {
let startTabCount = getTabs().length;
let tab = openTab(getMostRecentBrowserWindow(), 'about:blank', {
isPrivate: true
});
assert.ok(isPrivate(getTabContentWindow(tab)), 'new tab is private');
let utils_tabs = getTabs();
assert.equal(utils_tabs.length, startTabCount + 1,
'there are two tabs found');
assert.equal(utils_tabs[utils_tabs.length-1], tab,
'the last tab is the opened tab');
assert.equal(browserWindows.length, 1, 'there is only one window');
closeTab(tab);
done();
};
}
// require('test').run(exports);

View File

@ -0,0 +1,193 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
const tabs = require("sdk/tabs"); // From addon-kit
const windowUtils = require("sdk/deprecated/window-utils");
const { getTabForWindow } = require('sdk/tabs/helpers');
const app = require("sdk/system/xul-app");
const { viewFor } = require("sdk/view/core");
const { modelFor } = require("sdk/model/core");
const { getTabId, isTab } = require("sdk/tabs/utils");
const { defer } = require("sdk/lang/functional");
// The primary test tab
var primaryTab;
// We have an auxiliary tab to test background tabs.
var auxTab;
// The window for the outer iframe in the primary test page
var iframeWin;
exports["test GetTabForWindow"] = function(assert, done) {
assert.equal(getTabForWindow(windowUtils.activeWindow), null,
"getTabForWindow return null on topwindow");
assert.equal(getTabForWindow(windowUtils.activeBrowserWindow), null,
"getTabForWindow return null on topwindow");
let subSubDocument = encodeURIComponent(
'Sub iframe<br/>'+
'<iframe id="sub-sub-iframe" src="data:text/html;charset=utf-8,SubSubIframe" />');
let subDocument = encodeURIComponent(
'Iframe<br/>'+
'<iframe id="sub-iframe" src="data:text/html;charset=utf-8,'+subSubDocument+'" />');
let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(
'Content<br/><iframe id="iframe" src="data:text/html;charset=utf-8,'+subDocument+'" />');
// Open up a new tab in the background.
//
// This lets us test whether GetTabForWindow works even when the tab in
// question is not active.
tabs.open({
inBackground: true,
url: "about:mozilla",
onReady: function(tab) { auxTab = tab; step2(url, assert);},
onActivate: function(tab) { step3(assert, done); }
});
};
function step2(url, assert) {
tabs.open({
url: url,
onReady: function(tab) {
primaryTab = tab;
let window = windowUtils.activeBrowserWindow.content;
let matchedTab = getTabForWindow(window);
assert.equal(matchedTab, tab,
"We are able to find the tab with his content window object");
let timer = require("sdk/timers");
function waitForFrames() {
let iframe = window.document.getElementById("iframe");
if (!iframe) {
timer.setTimeout(waitForFrames, 100);
return;
}
iframeWin = iframe.contentWindow;
let subIframe = iframeWin.document.getElementById("sub-iframe");
if (!subIframe) {
timer.setTimeout(waitForFrames, 100);
return;
}
let subIframeWin = subIframe.contentWindow;
let subSubIframe = subIframeWin.document.getElementById("sub-sub-iframe");
if (!subSubIframe) {
timer.setTimeout(waitForFrames, 100);
return;
}
let subSubIframeWin = subSubIframe.contentWindow;
matchedTab = getTabForWindow(iframeWin);
assert.equal(matchedTab, tab,
"We are able to find the tab with an iframe window object");
matchedTab = getTabForWindow(subIframeWin);
assert.equal(matchedTab, tab,
"We are able to find the tab with a sub-iframe window object");
matchedTab = getTabForWindow(subSubIframeWin);
assert.equal(matchedTab, tab,
"We are able to find the tab with a sub-sub-iframe window object");
// Put our primary tab in the background and test again.
// The onActivate listener will take us to step3.
auxTab.activate();
}
waitForFrames();
}
});
}
function step3(assert, done) {
let matchedTab = getTabForWindow(iframeWin);
assert.equal(matchedTab, primaryTab,
"We get the correct tab even when it's in the background");
primaryTab.close(function () {
auxTab.close(function () { done();});
});
}
exports["test behavior on close"] = function(assert, done) {
tabs.open({
url: "about:mozilla",
onReady: function(tab) {
assert.equal(tab.url, "about:mozilla", "Tab has the expected url");
// if another test ends before closing a tab then index != 1 here
assert.ok(tab.index >= 1, "Tab has the expected index, a value greater than 0");
tab.close(function () {
assert.equal(tab.url, undefined,
"After being closed, tab attributes are undefined (url)");
assert.equal(tab.index, undefined,
"After being closed, tab attributes are undefined (index)");
if (app.is("Firefox")) {
// Ensure that we can call destroy multiple times without throwing;
// Fennec doesn't use this internal utility
tab.destroy();
tab.destroy();
}
done();
});
}
});
};
exports["test viewFor(tab)"] = (assert, done) => {
// Note we defer handlers as length collection is updated after
// handler is invoked, so if test is finished before counnts are
// updated wrong length will show up in followup tests.
tabs.once("open", defer(tab => {
const view = viewFor(tab);
assert.ok(view, "view is returned");
assert.equal(getTabId(view), tab.id, "tab has a same id");
tab.close(defer(done));
}));
tabs.open({ url: "about:mozilla" });
};
exports["test modelFor(xulTab)"] = (assert, done) => {
tabs.open({
url: "about:mozilla",
onReady: tab => {
const view = viewFor(tab);
assert.ok(view, "view is returned");
assert.ok(isTab(view), "view is underlaying tab");
assert.equal(getTabId(view), tab.id, "tab has a same id");
assert.equal(modelFor(view), tab, "modelFor(view) is SDK tab");
tab.close(defer(done));
}
});
};
exports["test tab.readyState"] = (assert, done) => {
tabs.open({
url: "data:text/html;charset=utf-8,test_readyState",
onOpen: (tab) => {
assert.equal(tab.readyState, "uninitialized",
"tab is 'uninitialized' when opened");
},
onReady: (tab) => {
assert.notEqual(["interactive", "complete"].indexOf(tab.readyState), -1,
"tab is either interactive or complete when onReady");
},
onLoad: (tab) => {
assert.equal(tab.readyState, "complete", "tab is complete onLoad");
tab.close(defer(done));
}
});
}
// require("sdk/test").run(exports);

View File

@ -0,0 +1,10 @@
{
"name": "e10s-tabs",
"title": "e10s-tabs",
"id": "jid1-ZZaXFHAPlHwbgw",
"description": "run tab tests in e10s mode",
"author": "Tomislav Jovanovic",
"license": "MPL 2.0",
"version": "0.1",
"e10s": true
}

View File

@ -10,6 +10,12 @@ const { openTab, closeTab, getBrowserForTab } = require("sdk/tabs/utils");
const { defer } = require("sdk/core/promise");
const { curry, identity, partial } = require("sdk/lang/functional");
const { nuke } = require("sdk/loader/sandbox");
const { open: openWindow, close: closeWindow } = require('sdk/window/helpers');
const openBrowserWindow = partial(openWindow, null, {features: {toolbar: true}});
let when = curry(function(options, tab) {
let type = options.type || options;
let capture = options.capture || false;
@ -116,6 +122,46 @@ exports["test nested frames"] = function(assert, done) {
});
};
exports["test dead object errors"] = function(assert, done) {
let system = require("sdk/system/events");
let loader = Loader(module);
let { events } = loader.require("sdk/content/events");
// The dead object error is properly reported on console but
// doesn't raise any test's exception
function onMessage({ subject }) {
let message = subject.wrappedJSObject;
let { level } = message;
let text = String(message.arguments[0]);
if (level === "error" && text.contains("can't access dead object"))
fail(text);
}
let cleanup = () => system.off("console-api-log-event", onMessage);
let fail = (reason) => {
cleanup();
assert.fail(reason);
}
loader.unload();
// in order to get a dead object error on this module, we need to nuke
// the relative sandbox; unload the loader is not enough
let url = Object.keys(loader.sandboxes).
find(url => url.endsWith("/sdk/content/events.js"));
nuke(loader.sandboxes[url]);
system.on("console-api-log-event", onMessage, true);
openBrowserWindow().
then(closeWindow).
then(() => assert.pass("checking dead object errors")).
then(cleanup).
then(done, fail);
};
// ignore *-document-global-created events that are not very consistent.
// only allow data uris that we create to ignore unwanted events, e.g.,
// about:blank, http:// requests from Fennec's `about:`home page that displays
@ -125,7 +171,7 @@ function eventFilter (type, target, callback) {
if (target.URL.startsWith("data:text/html,") &&
type !== "chrome-document-global-created" &&
type !== "content-document-global-created")
callback();
}
require("test").run(exports);

View File

@ -937,4 +937,31 @@ exports["test:global postMessage"] = WorkerTest(
});
});
exports["test:destroy unbinds listeners from port"] = WorkerTest(
"data:text/html;charset=utf-8,portdestroyer",
function(assert, browser, done) {
let destroyed = false;
let worker = Worker({
window: browser.contentWindow,
contentScript: "new " + function WorkerScope() {
self.port.emit("destroy");
setInterval(self.port.emit, 10, "ping");
},
onDestroy: done
});
worker.port.on("ping", () => {
if (destroyed) {
assert.fail("Should not call events on port after destroy.");
}
});
worker.port.on("destroy", () => {
destroyed = true;
worker.destroy();
assert.pass("Worker destroyed, waiting for no future listeners handling events.");
setTimeout(done, 500);
});
}
);
require("test").run(exports);

View File

@ -678,8 +678,10 @@ exports.testContentScriptWhenOnTabOpen = function(assert, done) {
// test timing for all 3 contentScriptWhen options (start, ready, end)
// for PageMods created while the tab is interactive (in tab.onReady)
exports.testContentScriptWhenOnTabReady = function(assert, done) {
const url = "data:text/html;charset=utf-8,testContentScriptWhenOnTabReady";
// need a bit bigger document to get the right timing of events with e10s
let iframeURL = 'data:text/html;charset=utf-8,testContentScriptWhenOnTabReady';
let iframe = '<iframe src="' + iframeURL + '" />';
let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(iframe);
tabs.open({
url: url,
onReady: function(tab) {

View File

@ -65,7 +65,7 @@ exports.testSidebarIsNotOpenInNewPrivateWindow = function(assert, done) {
assert.equal(isSidebarShowing(window), true, 'the sidebar is showing');
assert.equal(isShowing(sidebar), true, 'the sidebar is showing');
let window2 = window.OpenBrowserWindow({private: true});
let window2 = window.OpenBrowserWindow({ private: true });
windowPromise(window2, 'load').then(focus).then(function() {
// TODO: find better alt to setTimeout...
setTimeout(function() {

View File

@ -13,7 +13,7 @@ const { Cu } = require('chrome');
const { Loader } = require('sdk/test/loader');
const { show, hide } = require('sdk/ui/sidebar/actions');
const { isShowing } = require('sdk/ui/sidebar/utils');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { getMostRecentBrowserWindow, isFocused } = require('sdk/window/utils');
const { open, close, focus, promise: windowPromise } = require('sdk/window/helpers');
const { setTimeout, setImmediate } = require('sdk/timers');
const { isPrivate } = require('sdk/private-browsing');
@ -21,6 +21,7 @@ const data = require('./fixtures');
const { URL } = require('sdk/url');
const { once, off, emit } = require('sdk/event/core');
const { defer, all } = require('sdk/core/promise');
const { modelFor } = require('sdk/model/core');
const { BUILTIN_SIDEBAR_MENUITEMS, isSidebarShowing,
getSidebarMenuitems, getExtraSidebarMenuitems, makeID, simulateCommand,
@ -1419,7 +1420,7 @@ exports.testEventListeners = function(assert, done) {
// For more information see Bug 920780
exports.testAttachDoesNotEmitWhenShown = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testSidebarLeakCheckUnloadAfterAttach';
let testName = 'testAttachDoesNotEmitWhenShown';
let count = 0;
let sidebar = Sidebar({
@ -1459,6 +1460,73 @@ exports.testAttachDoesNotEmitWhenShown = function(assert, done) {
sidebar.show();
}
exports.testShowHideRawWindowArg = function*(assert) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testShowHideRawWindowArg';
let sidebar = Sidebar({
id: testName,
title: testName,
url: 'data:text/html;charset=utf-8,' + testName
});
let mainWindow = getMostRecentBrowserWindow();
let newWindow = yield open().then(focus);
yield focus(mainWindow);
yield sidebar.show(newWindow);
assert.pass('the sidebar was shown');
assert.ok(!isSidebarShowing(mainWindow), 'sidebar is not showing in main window');
assert.ok(isSidebarShowing(newWindow), 'sidebar is showing in new window');
assert.ok(isFocused(mainWindow), 'main window is still focused');
yield sidebar.hide(newWindow);
assert.ok(isFocused(mainWindow), 'main window is still focused');
assert.ok(!isSidebarShowing(mainWindow), 'sidebar is not showing in main window');
assert.ok(!isSidebarShowing(newWindow), 'sidebar is not showing in new window');
sidebar.destroy();
yield close(newWindow);
}
exports.testShowHideSDKWindowArg = function*(assert) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testShowHideSDKWindowArg';
let sidebar = Sidebar({
id: testName,
title: testName,
url: 'data:text/html;charset=utf-8,' + testName
});
let mainWindow = getMostRecentBrowserWindow();
let newWindow = yield open().then(focus);
let newSDKWindow = modelFor(newWindow);
yield focus(mainWindow);
yield sidebar.show(newSDKWindow);
assert.pass('the sidebar was shown');
assert.ok(!isSidebarShowing(mainWindow), 'sidebar is not showing in main window');
assert.ok(isSidebarShowing(newWindow), 'sidebar is showing in new window');
assert.ok(isFocused(mainWindow), 'main window is still focused');
yield sidebar.hide(newSDKWindow);
assert.ok(isFocused(mainWindow), 'main window is still focused');
assert.ok(!isSidebarShowing(mainWindow), 'sidebar is not showing in main window');
assert.ok(!isSidebarShowing(newWindow), 'sidebar is not showing in new window');
sidebar.destroy();
yield close(newWindow);
}
// If the module doesn't support the app we're being run in, require() will
// throw. In that case, remove all tests above from exports, and add one dummy
// test that passes.

View File

@ -1275,7 +1275,6 @@ let CustomizableUIInternal = {
shortcut = ShortcutUtils.findShortcut(document.getElementById(commandId));
}
if (!shortcut) {
ERROR("Could not find a keyboard shortcut for '" + aShortcutNode.outerHTML + "'.");
return;
}

View File

@ -52,7 +52,7 @@ function setAttributes(aNode, aAttrs) {
let additionalArgs = [];
if (aAttrs.shortcutId) {
let shortcut = doc.getElementById(aAttrs.shortcutId);
if (doc) {
if (shortcut) {
additionalArgs.push(ShortcutUtils.prettifyShortcut(shortcut));
}
}

View File

@ -284,7 +284,6 @@ var gPrivacyPane = {
if (shouldProceed) {
pref.value = autoStart.hasAttribute('checked');
document.documentElement.acceptDialog();
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
.getService(Ci.nsIAppStartup);
appStartup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);

View File

@ -14,7 +14,7 @@ add_task(function test_load_start() {
// Load a new URI but remove the tab before it has finished loading.
browser.loadURI("about:mozilla");
yield promiseContentMessage(browser, "ss-test:onFrameTreeReset");
yield promiseContentMessage(browser, "ss-test:OnHistoryReplaceEntry");
gBrowser.removeTab(tab);
// Undo close the tab.

View File

@ -7,6 +7,7 @@
let Cu = Components.utils;
let Ci = Components.interfaces;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/sessionstore/FrameTree.jsm", this);
let gFrameTree = new FrameTree(this);
@ -20,6 +21,47 @@ gFrameTree.addObserver({
}
});
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
webNav.sessionHistory.addSHistoryListener({
OnHistoryNewEntry: function () {
sendAsyncMessage("ss-test:OnHistoryNewEntry");
},
OnHistoryGoBack: function () {
sendAsyncMessage("ss-test:OnHistoryGoBack");
return true;
},
OnHistoryGoForward: function () {
sendAsyncMessage("ss-test:OnHistoryGoForward");
return true;
},
OnHistoryGotoIndex: function () {
sendAsyncMessage("ss-test:OnHistoryGotoIndex");
return true;
},
OnHistoryPurge: function () {
sendAsyncMessage("ss-test:OnHistoryPurge");
return true;
},
OnHistoryReload: function () {
sendAsyncMessage("ss-test:OnHistoryReload");
return true;
},
OnHistoryReplaceEntry: function () {
sendAsyncMessage("ss-test:OnHistoryReplaceEntry");
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsISHistoryListener,
Ci.nsISupportsWeakReference
])
});
/**
* This frame script is only loaded for sessionstore mochitests. It enables us
* to modify and query docShell data when running with multiple processes.

View File

@ -43,6 +43,9 @@ DebuggerPanel.prototype = {
// Local debugging needs to make the target remote.
if (!this.target.isRemote) {
targetPromise = this.target.makeRemote();
// Listen for tab switching events to manage focus when the content window
// is paused and events suppressed.
this.target.tab.addEventListener('TabSelect', this);
} else {
targetPromise = promise.resolve(this.target);
}
@ -76,6 +79,10 @@ DebuggerPanel.prototype = {
this.target.off("thread-paused", this.highlightWhenPaused);
this.target.off("thread-resumed", this.unhighlightWhenResumed);
if (!this.target.isRemote) {
this.target.tab.removeEventListener('TabSelect', this);
}
return this._destroyer = this._controller.shutdownDebugger().then(() => {
this.emit("destroyed");
});
@ -105,5 +112,15 @@ DebuggerPanel.prototype = {
unhighlightWhenResumed: function() {
this._toolbox.unhighlightTool("jsdebugger");
},
// nsIDOMEventListener API
handleEvent: function(aEvent) {
if (aEvent.target == this.target.tab &&
this._controller.activeThread.state == "paused") {
// Wait a tick for the content focus event to be delivered.
DevToolsUtils.executeSoon(() => this._toolbox.focusTool("jsdebugger"));
}
}
};

View File

@ -190,6 +190,7 @@ skip-if = os == "linux" || e10s # Bug 888811 & bug 891176
[browser_dbg_pause-exceptions-02.js]
[browser_dbg_pause-resume.js]
[browser_dbg_pause-warning.js]
[browser_dbg_paused-keybindings.js]
[browser_dbg_pretty-print-01.js]
[browser_dbg_pretty-print-02.js]
[browser_dbg_pretty-print-03.js]

View File

@ -0,0 +1,45 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that keybindings still work when the content window is paused and
// the tab is selected again.
function test() {
Task.spawn(function* () {
const TAB_URL = EXAMPLE_URL + "doc_inline-script.html";
let panel, debuggee, gDebugger, searchBox;
let [, debuggee, panel] = yield initDebugger(TAB_URL);
gDebugger = panel.panelWin;
searchBox = gDebugger.DebuggerView.Filtering._searchbox;
// Spin the event loop before causing the debuggee to pause, to allow
// this function to return first.
executeSoon(() => {
EventUtils.sendMouseEvent({ type: "click" },
debuggee.document.querySelector("button"),
debuggee);
});
yield waitForSourceAndCaretAndScopes(panel, ".html", 20);
yield ensureThreadClientState(panel, "paused");
// Now open a tab and close it.
let tab2 = yield addTab(TAB_URL);
yield removeTab(tab2);
yield ensureCaretAt(panel, 20);
// Try to use the Cmd-L keybinding to see if it still works.
let caretMove = ensureCaretAt(panel, 15, 1, true);
// Wait a tick for the editor focus event to occur first.
executeSoon(function () {
EventUtils.synthesizeKey("l", { accelKey: true });
EventUtils.synthesizeKey("1", {});
EventUtils.synthesizeKey("5", {});
});
yield caretMove;
yield resumeDebuggerThenCloseAndFinish(panel);
}).then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
}

View File

@ -1,110 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
let doc;
let salutation;
let closing;
const NEWHEIGHT = 226;
function createDocument()
{
doc.body.innerHTML = '<div id="first" style="{ margin: 10em; ' +
'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA}">\n' +
'<h1>Some header text</h1>\n' +
'<p id="salutation" style="{font-size: 12pt}">hi.</p>\n' +
'<p id="body" style="{font-size: 12pt}">I am a test-case. This text exists ' +
'solely to provide some things to test the inspector initialization.</p>\n' +
'If you are reading this, you should go do something else instead. Maybe ' +
'read a book. Or better yet, write some test-cases for another bit of code. ' +
'<span style="{font-style: italic}">Maybe more inspector test-cases!</span></p>\n' +
'<p id="closing">end transmission</p>\n' +
'</div>';
doc.title = "Inspector Initialization Test";
startInspectorTests();
}
function startInspectorTests()
{
ok(InspectorUI, "InspectorUI variable exists");
Services.obs.addObserver(runInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.toggleInspectorUI();
}
function runInspectorTests()
{
Services.obs.removeObserver(runInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
if (InspectorUI.treePanelEnabled) {
Services.obs.addObserver(treePanelTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
InspectorUI.stopInspecting();
InspectorUI.treePanel.open();
} else
finishInspectorTests();
}
function treePanelTests()
{
Services.obs.removeObserver(treePanelTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
Services.obs.addObserver(treePanelTests2,
InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
let height = Services.prefs.getIntPref("devtools.inspector.htmlHeight");
is(InspectorUI.treePanel.container.height, height,
"Container height is " + height);
InspectorUI.treePanel.container.height = NEWHEIGHT;
executeSoon(function() {
InspectorUI.treePanel.close();
InspectorUI.treePanel.open();
});
}
function treePanelTests2()
{
Services.obs.removeObserver(treePanelTests2,
InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
let height = Services.prefs.getIntPref("devtools.inspector.htmlHeight");
is(InspectorUI.treePanel.container.height, NEWHEIGHT,
"Container height is now " + height);
InspectorUI.treePanel.close();
executeSoon(function() {
finishInspectorTests()
});
}
function finishInspectorTests()
{
gBrowser.removeCurrentTab();
finish();
}
function test()
{
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
doc = content.document;
waitForFocus(createDocument, content);
}, true);
content.location = "data:text/html;charset=utf-8,browser_inspector_tree_height.js";
}

View File

@ -181,6 +181,7 @@ HTMLEditor.prototype = {
this.editorInner.removeEventListener("click", stopPropagation, false);
this.hide(false);
this.container.parentNode.removeChild(this.container);
this.container.remove();
this.editor.destroy();
}
};

View File

@ -118,7 +118,7 @@ let NetMonitorView = {
/**
* Destroys the UI for all the displayed panes.
*/
_destroyPanes: function() {
_destroyPanes: Task.async(function*() {
dumpn("Destroying the NetMonitorView panes");
Prefs.networkDetailsWidth = this._detailsPane.getAttribute("width");
@ -126,7 +126,12 @@ let NetMonitorView = {
this._detailsPane = null;
this._detailsPaneToggleButton = null;
},
for (let p of this._editorPromises.values()) {
let editor = yield p;
editor.destroy();
}
}),
/**
* Gets the visibility state of the network details pane.

View File

@ -40,6 +40,11 @@ var ItchEditor = Class({
emit(this, name, ...args);
},
/* Does the editor not have any unsaved changes? */
isClean: function() {
return true;
},
/**
* Initialize the editor with a single host. This should be called
* by objects extending this object with:
@ -145,6 +150,13 @@ var TextEditor = Class({
return extraKeys;
},
isClean: function() {
if (!this.editor.isAppended()) {
return true;
}
return this.editor.isClean();
},
initialize: function(document, mode=Editor.modes.text) {
ItchEditor.prototype.initialize.apply(this, arguments);
this.label = mode.name;
@ -165,11 +177,6 @@ var TextEditor = Class({
});
this.appended = this.editor.appendTo(this.elt);
this.appended.then(() => {
if (this.editor) {
this.editor.setupAutoCompletion();
}
});
},
/**

View File

@ -17,7 +17,7 @@ var DeletePlugin = Class({
this.host.addCommand(this, {
id: "cmd-delete"
});
this.host.createMenuItem({
this.contextMenuItem = this.host.createMenuItem({
parent: this.host.contextMenuPopup,
label: getLocalizedString("projecteditor.deleteLabel"),
command: "cmd-delete"
@ -34,6 +34,19 @@ var DeletePlugin = Class({
);
},
onContextMenuOpen: function(resource) {
// Do not allow deletion of the top level items in the tree. In the
// case of the Web IDE in particular this can leave the UI in a weird
// state. If we'd like to add ability to delete the project folder from
// the tree in the future, then the UI could be cleaned up by listening
// to the ProjectTree's "resource-removed" event.
if (!resource.parent) {
this.contextMenuItem.setAttribute("hidden", "true");
} else {
this.contextMenuItem.removeAttribute("hidden");
}
},
onCommand: function(cmd) {
if (cmd === "cmd-delete") {
let tree = this.host.projectTree;

View File

@ -22,9 +22,8 @@ var DirtyPlugin = Class({
// Dont' force a refresh unless the dirty state has changed...
let priv = this.priv(editor);
let clean = editor.editor.isClean();
let clean = editor.isClean()
if (priv.isClean !== clean) {
let resource = editor.shell.resource;
emit(resource, "label-change", resource);
priv.isClean = clean;

View File

@ -62,6 +62,7 @@ require("projecteditor/plugins/status-bar/plugin");
* - "onEditorCursorActivity": When there is cursor activity in a text editor
* - "onCommand": When a command happens
* - "onEditorDestroyed": When editor is destroyed
* - "onContextMenuOpen": When the context menu is opened on the project tree
*
* The events can be bound like so:
* projecteditor.on("onEditorCreated", (editor) => { });
@ -87,6 +88,7 @@ var ProjectEditor = Class({
this._onEditorActivated = this._onEditorActivated.bind(this);
this._onEditorDeactivated = this._onEditorDeactivated.bind(this);
this._updateMenuItems = this._updateMenuItems.bind(this);
this._updateContextMenuItems = this._updateContextMenuItems.bind(this);
this.destroy = this.destroy.bind(this);
this.menubar = options.menubar || null;
this.menuindex = options.menuindex || null;
@ -231,6 +233,7 @@ var ProjectEditor = Class({
this.editorKeyset = this.document.getElementById("editMenuKeys");
this.contextMenuPopup = this.document.getElementById("context-menu-popup");
this.contextMenuPopup.addEventListener("popupshowing", this._updateContextMenuItems);
this.projectEditorCommandset.addEventListener("command", (evt) => {
evt.stopPropagation();
@ -269,6 +272,15 @@ var ProjectEditor = Class({
}
},
/**
* Enable / disable necessary context menu items by passing an event
* onto plugins.
*/
_updateContextMenuItems: function() {
let resource = this.projectTree.getSelectedResource();
this.pluginDispatch("onContextMenuOpen", resource);
},
/**
* Destroy all objects on the iframe unload event.
*/

View File

@ -192,7 +192,10 @@ var ShellDeck = Class({
this.deck.selectedPanel = shell.elt;
this._activeShell = shell;
shell.load();
// Only reload the shell if the editor doesn't have local changes.
if (shell.editor.isClean()) {
shell.load();
}
shell.editorLoaded.then(() => {
// Handle case where another shell has been requested before this
// one is finished loading.

View File

@ -13,68 +13,73 @@ let test = asyncTest(function*() {
let root = [...projecteditor.project.allStores()][0].root;
is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
for (let child of root.children) {
yield deleteWithContextMenu(projecteditor.projectTree.getViewContainer(child));
yield deleteWithContextMenu(projecteditor, projecteditor.projectTree.getViewContainer(child));
}
function onPopupShow(contextMenu) {
let defer = promise.defer();
contextMenu.addEventListener("popupshown", function onpopupshown() {
contextMenu.removeEventListener("popupshown", onpopupshown);
defer.resolve();
});
return defer.promise;
}
yield testDeleteOnRoot(projecteditor, projecteditor.projectTree.getViewContainer(root));
});
function openContextMenuOn(node) {
EventUtils.synthesizeMouseAtCenter(
node,
{button: 2, type: "contextmenu"},
node.ownerDocument.defaultView
);
}
function deleteWithContextMenu(container) {
let defer = promise.defer();
function openContextMenuOn(node) {
EventUtils.synthesizeMouseAtCenter(
node,
{button: 2, type: "contextmenu"},
node.ownerDocument.defaultView
);
}
let resource = container.resource;
let popup = projecteditor.contextMenuPopup;
info ("Going to attempt deletion for: " + resource.path)
function testDeleteOnRoot(projecteditor, container) {
let popup = projecteditor.contextMenuPopup;
let oncePopupShown = onPopupShow(popup);
openContextMenuOn(container.label);
yield oncePopupShown;
onPopupShow(popup).then(function () {
let deleteCommand = popup.querySelector("[command=cmd-delete]");
ok (deleteCommand, "Delete command exists in popup");
is (deleteCommand.getAttribute("hidden"), "", "Delete command is visible");
is (deleteCommand.getAttribute("disabled"), "", "Delete command is enabled");
let deleteCommand = popup.querySelector("[command=cmd-delete]");
ok (deleteCommand, "Delete command exists in popup");
is (deleteCommand.getAttribute("hidden"), "true", "Delete command is hidden");
}
function onConfirmShown(aSubject) {
info("confirm dialog observed as expected");
Services.obs.removeObserver(onConfirmShown, "common-dialog-loaded");
Services.obs.removeObserver(onConfirmShown, "tabmodal-dialog-loaded");
function deleteWithContextMenu(projecteditor, container) {
let defer = promise.defer();
projecteditor.project.on("refresh-complete", function refreshComplete() {
projecteditor.project.off("refresh-complete", refreshComplete);
OS.File.stat(resource.path).then(() => {
ok (false, "The file was not deleted");
defer.resolve();
}, (ex) => {
ok (ex instanceof OS.File.Error && ex.becauseNoSuchFile, "OS.File.stat promise was rejected because the file is gone");
defer.resolve();
});
let popup = projecteditor.contextMenuPopup;
let resource = container.resource;
info ("Going to attempt deletion for: " + resource.path);
onPopupShow(popup).then(function () {
let deleteCommand = popup.querySelector("[command=cmd-delete]");
ok (deleteCommand, "Delete command exists in popup");
is (deleteCommand.getAttribute("hidden"), "", "Delete command is visible");
is (deleteCommand.getAttribute("disabled"), "", "Delete command is enabled");
function onConfirmShown(aSubject) {
info("confirm dialog observed as expected");
Services.obs.removeObserver(onConfirmShown, "common-dialog-loaded");
Services.obs.removeObserver(onConfirmShown, "tabmodal-dialog-loaded");
projecteditor.project.on("refresh-complete", function refreshComplete() {
projecteditor.project.off("refresh-complete", refreshComplete);
OS.File.stat(resource.path).then(() => {
ok (false, "The file was not deleted");
defer.resolve();
}, (ex) => {
ok (ex instanceof OS.File.Error && ex.becauseNoSuchFile, "OS.File.stat promise was rejected because the file is gone");
defer.resolve();
});
});
// Click the 'OK' button
aSubject.Dialog.ui.button0.click();
}
// Click the 'OK' button
aSubject.Dialog.ui.button0.click();
}
Services.obs.addObserver(onConfirmShown, "common-dialog-loaded", false);
Services.obs.addObserver(onConfirmShown, "tabmodal-dialog-loaded", false);
Services.obs.addObserver(onConfirmShown, "common-dialog-loaded", false);
Services.obs.addObserver(onConfirmShown, "tabmodal-dialog-loaded", false);
deleteCommand.click();
popup.hidePopup();
});
deleteCommand.click();
popup.hidePopup();
});
openContextMenuOn(container.label);
openContextMenuOn(container.label);
return defer.promise;
}
});
return defer.promise;
}

View File

@ -22,9 +22,41 @@ let test = asyncTest(function*() {
let resource = resources.filter(r=>r.basename === data.basename)[0];
yield selectFile(projecteditor, resource);
yield testChangeFileExternally(projecteditor, getTempFile(data.path).path, data.newContent);
yield testChangeUnsavedFileExternally(projecteditor, getTempFile(data.path).path, data.newContent + "[changed]");
}
});
function testChangeUnsavedFileExternally(projecteditor, filePath, newData) {
info ("Testing file external changes for: " + filePath);
let editor = projecteditor.currentEditor;
let resource = projecteditor.resourceFor(editor);
let initialData = yield getFileData(filePath);
is (resource.path, filePath, "Resource path is set correctly");
is (editor.editor.getText(), initialData, "Editor is loaded with correct file contents");
info ("Editing but not saving file in project editor");
ok (editor.isClean(), "Editor is clean");
editor.editor.setText("foobar");
ok (!editor.isClean(), "Editor is dirty");
info ("Editor has been selected, writing to file externally");
yield writeToFile(resource.path, newData);
info ("Selecting another resource, then reselecting this one");
projecteditor.projectTree.selectResource(resource.store.root);
yield onceEditorActivated(projecteditor);
projecteditor.projectTree.selectResource(resource);
yield onceEditorActivated(projecteditor);
let editor = projecteditor.currentEditor;
info ("Checking to make sure the editor is now populated correctly");
is (editor.editor.getText(), "foobar", "Editor has not been updated with new file contents");
info ("Finished checking saving for " + filePath);
}
function testChangeFileExternally(projecteditor, filePath, newData) {
info ("Testing file external changes for: " + filePath);

View File

@ -106,21 +106,3 @@ function openAndCloseMenu(menu) {
EventUtils.synthesizeMouseAtCenter(menu, {}, menu.ownerDocument.defaultView);
yield hidden;
}
function onPopupShow(menu) {
let defer = promise.defer();
menu.addEventListener("popupshown", function onpopupshown() {
menu.removeEventListener("popupshown", onpopupshown);
defer.resolve();
});
return defer.promise;
}
function onPopupHidden(menu) {
let defer = promise.defer();
menu.addEventListener("popuphidden", function onpopupshown() {
menu.removeEventListener("popuphidden", onpopupshown);
defer.resolve();
});
return defer.promise;
}

View File

@ -303,3 +303,22 @@ function onceEditorSave(projecteditor) {
});
return def.promise;
}
function onPopupShow(menu) {
let defer = promise.defer();
menu.addEventListener("popupshown", function onpopupshown() {
menu.removeEventListener("popupshown", onpopupshown);
defer.resolve();
});
return defer.promise;
}
function onPopupHidden(menu) {
let defer = promise.defer();
menu.addEventListener("popuphidden", function onpopuphidden() {
menu.removeEventListener("popuphidden", onpopuphidden);
defer.resolve();
});
return defer.promise;
}

View File

@ -1613,7 +1613,6 @@ var Scratchpad = {
this.editor.appendTo(editorElement).then(() => {
var lines = initialText.split("\n");
this.editor.setupAutoCompletion();
this.editor.on("change", this._onChanged);
this._onPaste = WebConsoleUtils.pasteHandlerGen(this.editor.container.contentDocument.body,
document.querySelector('#scratchpad-notificationbox'));
@ -2323,6 +2322,12 @@ var CloseObserver = {
uninit: function CO_uninit()
{
// Will throw exception if removeObserver is called twice.
if (this._uninited) {
return;
}
this._uninited = true;
Services.obs.removeObserver(this, "browser-lastwindow-close-requested",
false);
},

View File

@ -359,9 +359,14 @@ let ShadersEditorsView = {
/**
* Destruction function, called when the tool is closed.
*/
destroy: function() {
destroy: Task.async(function*() {
this._destroyed = true;
this._toggleListeners("off");
},
for (let p of this._editorPromises.values()) {
let editor = yield p;
editor.destroy();
}
}),
/**
* Sets the text displayed in the vertex and fragment shader editors.
@ -415,7 +420,12 @@ let ShadersEditorsView = {
let parent = $("#" + type +"-editor");
let editor = new Editor(DEFAULT_EDITOR_CONFIG);
editor.config.mode = Editor.modes[type];
editor.appendTo(parent).then(() => deferred.resolve(editor));
if (this._destroyed) {
deferred.resolve(editor);
} else {
editor.appendTo(parent).then(() => deferred.resolve(editor));
}
return deferred.promise;
},

View File

@ -11,14 +11,14 @@ const CM_TERN_SCRIPTS = [
"chrome://browser/content/devtools/codemirror/show-hint.js"
];
const privates = new WeakMap();
const autocompleteMap = new WeakMap();
/**
* Prepares an editor instance for autocompletion.
*/
function initializeAutoCompletion(ctx, options = {}) {
let { cm, ed, Editor } = ctx;
if (privates.has(ed)) {
if (autocompleteMap.has(ed)) {
return;
}
@ -88,12 +88,12 @@ function initializeAutoCompletion(ctx, options = {}) {
cm.off("cursorActivity", updateArgHintsCallback);
cm.removeKeyMap(keyMap);
win.tern = cm.tern = null;
privates.delete(ed);
autocompleteMap.delete(ed);
};
ed.on("destroy", destroyTern);
privates.set(ed, {
autocompleteMap.set(ed, {
destroy: destroyTern
});
@ -126,8 +126,8 @@ function initializeAutoCompletion(ctx, options = {}) {
"Up": cycle.bind(null, true),
"Enter": () => {
if (popup && popup.isOpen) {
if (!privates.get(ed).suggestionInsertedOnce) {
privates.get(ed).insertingSuggestion = true;
if (!autocompleteMap.get(ed).suggestionInsertedOnce) {
autocompleteMap.get(ed).insertingSuggestion = true;
let {label, preLabel, text} = popup.getItemAtIndex(0);
let cur = ed.getCursor();
ed.replaceText(text.slice(preLabel.length), cur, cur);
@ -157,10 +157,10 @@ function initializeAutoCompletion(ctx, options = {}) {
cm.removeKeyMap(keyMap);
popup.destroy();
keyMap = popup = completer = null;
privates.delete(ed);
autocompleteMap.delete(ed);
}
privates.set(ed, {
autocompleteMap.set(ed, {
popup: popup,
completer: completer,
keyMap: keyMap,
@ -175,11 +175,11 @@ function initializeAutoCompletion(ctx, options = {}) {
*/
function destroyAutoCompletion(ctx) {
let { ed } = ctx;
if (!privates.has(ed)) {
if (!autocompleteMap.has(ed)) {
return;
}
let {destroy} = privates.get(ed);
let {destroy} = autocompleteMap.get(ed);
destroy();
}
@ -187,7 +187,7 @@ function destroyAutoCompletion(ctx) {
* Provides suggestions to autocomplete the current token/word being typed.
*/
function autoComplete({ ed, cm }) {
let private = privates.get(ed);
let private = autocompleteMap.get(ed);
let { completer, popup } = private;
if (!completer || private.insertingSuggestion || private.doNotAutocomplete) {
private.insertingSuggestion = false;
@ -223,7 +223,7 @@ function autoComplete({ ed, cm }) {
* when `reverse` is not true. Opposite otherwise.
*/
function cycleSuggestions(ed, reverse) {
let private = privates.get(ed);
let private = autocompleteMap.get(ed);
let { popup, completer } = private;
let cur = ed.getCursor();
private.insertingSuggestion = true;
@ -263,7 +263,7 @@ function cycleSuggestions(ed, reverse) {
* keypresses.
*/
function onEditorKeypress({ ed, Editor }, cm, event) {
let private = privates.get(ed);
let private = autocompleteMap.get(ed);
// Do not try to autocomplete with multiple selections.
if (ed.hasMultipleSelections()) {
@ -319,8 +319,8 @@ function onEditorKeypress({ ed, Editor }, cm, event) {
* Returns the private popup. This method is used by tests to test the feature.
*/
function getPopup({ ed }) {
if (privates.has(ed))
return privates.get(ed).popup;
if (autocompleteMap.has(ed))
return autocompleteMap.get(ed).popup;
return null;
}
@ -330,16 +330,25 @@ function getPopup({ ed }) {
* implementation of completer supports it.
*/
function getInfoAt({ ed }, caret) {
let completer = privates.get(ed).completer;
let completer = autocompleteMap.get(ed).completer;
if (completer && completer.getInfoAt)
return completer.getInfoAt(ed.getText(), caret);
return null;
}
/**
* Returns whether autocompletion is enabled for this editor.
* Used for testing
*/
function isAutocompletionEnabled({ ed }) {
return autocompleteMap.has(ed);
}
// Export functions
module.exports.initializeAutoCompletion = initializeAutoCompletion;
module.exports.destroyAutoCompletion = destroyAutoCompletion;
module.exports.getAutocompletionPopup = getPopup;
module.exports.getInfoAt = getInfoAt;
module.exports.isAutocompletionEnabled = isAutocompletionEnabled;

View File

@ -17,6 +17,7 @@ const DETECT_INDENT = "devtools.editor.detectindentation";
const DETECT_INDENT_MAX_LINES = 500;
const L10N_BUNDLE = "chrome://browser/locale/devtools/sourceeditor.properties";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const VALID_KEYMAPS = new Set(["emacs", "vim", "sublime"]);
// Maximum allowed margin (in number of lines) from top or bottom of the editor
// while shifting to a line which was initially out of view.
@ -29,6 +30,7 @@ const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const events = require("devtools/toolkit/event-emitter");
const { PrefObserver } = require("devtools/styleeditor/utils");
Cu.import("resource://gre/modules/Services.jsm");
const L10N = Services.strings.createBundle(L10N_BUNDLE);
@ -139,7 +141,6 @@ Editor.modes = {
function Editor(config) {
const tabSize = Services.prefs.getIntPref(TAB_SIZE);
const useTabs = !Services.prefs.getBoolPref(EXPAND_TAB);
const keyMap = Services.prefs.getCharPref(KEYMAP);
const useAutoClose = Services.prefs.getBoolPref(AUTO_CLOSE);
this.version = null;
@ -157,7 +158,8 @@ function Editor(config) {
autoCloseEnabled: useAutoClose,
theme: "mozilla",
themeSwitching: true,
autocomplete: false
autocomplete: false,
autocompleteOpts: {}
};
// Additional shortcuts.
@ -170,9 +172,6 @@ function Editor(config) {
this.config.extraKeys[Editor.keyFor("indentLess")] = false;
this.config.extraKeys[Editor.keyFor("indentMore")] = false;
// If alternative keymap is provided, use it.
if (keyMap === "emacs" || keyMap === "vim" || keyMap === "sublime")
this.config.keyMap = keyMap;
// Overwrite default config with user-provided, if needed.
Object.keys(config).forEach((k) => {
@ -199,9 +198,8 @@ function Editor(config) {
}
}
// Configure automatic bracket closing.
if (!this.config.autoCloseEnabled)
this.config.autoCloseBrackets = false;
// Remember the initial value of autoCloseBrackets.
this.config.autoCloseBracketsSaved = this.config.autoCloseBrackets;
// Overwrite default tab behavior. If something is selected,
// indent those lines. If nothing is selected and we're
@ -325,8 +323,16 @@ Editor.prototype = {
this.container = env;
editors.set(this, cm);
this.resetIndentUnit();
this.reloadPreferences = this.reloadPreferences.bind(this);
this._prefObserver = new PrefObserver("devtools.editor.");
this._prefObserver.on(TAB_SIZE, this.reloadPreferences);
this._prefObserver.on(EXPAND_TAB, this.reloadPreferences);
this._prefObserver.on(KEYMAP, this.reloadPreferences);
this._prefObserver.on(AUTO_CLOSE, this.reloadPreferences);
this._prefObserver.on(AUTOCOMPLETE, this.reloadPreferences);
this._prefObserver.on(DETECT_INDENT, this.reloadPreferences);
this.reloadPreferences();
def.resolve();
};
@ -338,6 +344,14 @@ Editor.prototype = {
return def.promise;
},
/**
* Returns a boolean indicating whether the editor is ready to
* use. Use appendTo(el).then(() => {}) for most cases
*/
isAppended: function() {
return editors.has(this);
},
/**
* Returns the currently active highlighting mode.
* See Editor.modes for the list of all suppoert modes.
@ -397,6 +411,28 @@ Editor.prototype = {
this.resetIndentUnit();
},
/**
* Reload the state of the editor based on all current preferences.
* This is called automatically when any of the relevant preferences
* change.
*/
reloadPreferences: function() {
// Restore the saved autoCloseBrackets value if it is preffed on.
let useAutoClose = Services.prefs.getBoolPref(AUTO_CLOSE);
this.setOption("autoCloseBrackets",
useAutoClose ? this.config.autoCloseBracketsSaved : false);
// If alternative keymap is provided, use it.
const keyMap = Services.prefs.getCharPref(KEYMAP);
if (VALID_KEYMAPS.has(keyMap))
this.setOption("keyMap", keyMap)
else
this.setOption("keyMap", "default");
this.resetIndentUnit();
this.setupAutoCompletion();
},
/**
* Set the editor's indentation based on the current prefs and
* re-detect indentation if we should.
@ -878,6 +914,13 @@ Editor.prototype = {
*/
setOption: function(o, v) {
let cm = editors.get(this);
// Save the state of a valid autoCloseBrackets string, so we can reset
// it if it gets preffed off and back on.
if (o === "autoCloseBrackets" && v) {
this.config.autoCloseBracketsSaved = v;
}
if (o === "autocomplete") {
this.config.autocomplete = v;
this.setupAutoCompletion();
@ -908,7 +951,7 @@ Editor.prototype = {
* it just because it is preffed on (it still needs to be requested by the
* editor), but we do want to always disable it if it is preffed off.
*/
setupAutoCompletion: function (options = {}) {
setupAutoCompletion: function () {
// The autocomplete module will overwrite this.initializeAutoCompletion
// with a mode specific autocompletion handler.
if (!this.initializeAutoCompletion) {
@ -916,7 +959,7 @@ Editor.prototype = {
}
if (this.config.autocomplete && Services.prefs.getBoolPref(AUTOCOMPLETE)) {
this.initializeAutoCompletion(options);
this.initializeAutoCompletion(this.config.autocompleteOpts);
} else {
this.destroyAutoCompletion();
}
@ -957,6 +1000,17 @@ Editor.prototype = {
this.container = null;
this.config = null;
this.version = null;
if (this._prefObserver) {
this._prefObserver.off(TAB_SIZE, this.reloadPreferences);
this._prefObserver.off(EXPAND_TAB, this.reloadPreferences);
this._prefObserver.off(KEYMAP, this.reloadPreferences);
this._prefObserver.off(AUTO_CLOSE, this.reloadPreferences);
this._prefObserver.off(AUTOCOMPLETE, this.reloadPreferences);
this._prefObserver.off(DETECT_INDENT, this.reloadPreferences);
this._prefObserver.destroy();
}
this.emit("destroy");
}
};

View File

@ -28,6 +28,7 @@ support-files =
[browser_editor_history.js]
[browser_editor_markers.js]
[browser_editor_movelines.js]
[browser_editor_prefs.js]
[browser_editor_addons.js]
[browser_codemirror.js]
[browser_css_autocompletion.js]

View File

@ -53,8 +53,6 @@ function testPref(ed, win) {
info ("Preffing autocompletion off");
Services.prefs.setBoolPref(AUTOCOMPLETION_PREF, false);
ed.setupAutoCompletion();
ok (ed.getOption("autocomplete"), "Autocompletion is still set");
ok (!win.tern, "Tern is no longer defined on the window");

View File

@ -0,0 +1,78 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test to make sure that the editor reacts to preference changes
const TAB_SIZE = "devtools.editor.tabsize";
const EXPAND_TAB = "devtools.editor.expandtab";
const KEYMAP = "devtools.editor.keymap";
const AUTO_CLOSE = "devtools.editor.autoclosebrackets";
const AUTOCOMPLETE = "devtools.editor.autocomplete";
const DETECT_INDENT = "devtools.editor.detectindentation";
function test() {
waitForExplicitFinish();
setup((ed, win) => {
ed.setText("Checking preferences.");
info ("Turning prefs off");
ed.setOption("autocomplete", true);
Services.prefs.setIntPref(TAB_SIZE, 2);
Services.prefs.setBoolPref(EXPAND_TAB, false);
Services.prefs.setCharPref(KEYMAP, "default");
Services.prefs.setBoolPref(AUTO_CLOSE, false);
Services.prefs.setBoolPref(AUTOCOMPLETE, false);
Services.prefs.setBoolPref(DETECT_INDENT, false);
is(ed.getOption("tabSize"), 2, "tabSize is correct");
is(ed.getOption("indentUnit"), 2, "indentUnit is correct");
is(ed.getOption("indentWithTabs"), true, "indentWithTabs is correct");
is(ed.getOption("keyMap"), "default", "keyMap is correct");
is(ed.getOption("autoCloseBrackets"), "", "autoCloseBrackets is correct");
is(ed.getOption("autocomplete"), true, "autocomplete is correct");
ok(!ed.isAutocompletionEnabled(), "Autocompletion is not enabled");
info ("Turning prefs on");
Services.prefs.setIntPref(TAB_SIZE, 4);
Services.prefs.setBoolPref(EXPAND_TAB, true);
Services.prefs.setCharPref(KEYMAP, "sublime");
Services.prefs.setBoolPref(AUTO_CLOSE, true);
Services.prefs.setBoolPref(AUTOCOMPLETE, true);
is(ed.getOption("tabSize"), 4, "tabSize is correct");
is(ed.getOption("indentUnit"), 4, "indentUnit is correct");
is(ed.getOption("indentWithTabs"), false, "indentWithTabs is correct");
is(ed.getOption("keyMap"), "sublime", "keyMap is correct");
is(ed.getOption("autoCloseBrackets"), "()[]{}''\"\"", "autoCloseBrackets is correct");
is(ed.getOption("autocomplete"), true, "autocomplete is correct");
ok(ed.isAutocompletionEnabled(), "Autocompletion is enabled");
info ("Checking indentation detection");
Services.prefs.setBoolPref(DETECT_INDENT, true);
ed.setText("Detecting\n\tTabs");
is(ed.getOption("indentWithTabs"), true, "indentWithTabs is correct");
is(ed.getOption("indentUnit"), 4, "indentUnit is correct");
ed.setText("body {\n color:red;\n a:b;\n}");
is(ed.getOption("indentWithTabs"), false, "indentWithTabs is correct");
is(ed.getOption("indentUnit"), 2, "indentUnit is correct");
Services.prefs.clearUserPref(TAB_SIZE);
Services.prefs.clearUserPref(EXPAND_TAB);
Services.prefs.clearUserPref(KEYMAP);
Services.prefs.clearUserPref(AUTO_CLOSE);
Services.prefs.clearUserPref(AUTOCOMPLETE);
Services.prefs.clearUserPref(DETECT_INDENT);
teardown(ed, win);
});
}

View File

@ -94,6 +94,8 @@ function StyleSheetEditor(styleSheet, win, file, isNew, walker) {
this._onMediaRulesChanged = this._onMediaRulesChanged.bind(this)
this.checkLinkedFileForChanges = this.checkLinkedFileForChanges.bind(this);
this.markLinkedFileBroken = this.markLinkedFileBroken.bind(this);
this.saveToFile = this.saveToFile.bind(this);
this.updateStyleSheet = this.updateStyleSheet.bind(this);
this._focusOnSourceEditorReady = false;
this.cssSheet.on("property-change", this._onPropertyChange);
@ -347,23 +349,18 @@ StyleSheetEditor.prototype = {
autoCloseBrackets: "{}()[]",
extraKeys: this._getKeyBindings(),
contextMenu: "sourceEditorContextMenu",
autocomplete: Services.prefs.getBoolPref(AUTOCOMPLETION_PREF)
autocomplete: Services.prefs.getBoolPref(AUTOCOMPLETION_PREF),
autocompleteOpts: { walker: this.walker }
};
let sourceEditor = new Editor(config);
let sourceEditor = this._sourceEditor = new Editor(config);
sourceEditor.on("dirty-change", this._onPropertyChange);
return sourceEditor.appendTo(inputElement).then(() => {
sourceEditor.setupAutoCompletion({ walker: this.walker });
sourceEditor.on("save", () => {
this.saveToFile();
});
sourceEditor.on("save", this.saveToFile);
if (this.styleSheet.update) {
sourceEditor.on("change", () => {
this.updateStyleSheet();
});
sourceEditor.on("change", this.updateStyleSheet);
}
this.sourceEditor = sourceEditor;
@ -631,8 +628,11 @@ StyleSheetEditor.prototype = {
* Clean up for this editor.
*/
destroy: function() {
if (this.sourceEditor) {
this.sourceEditor.destroy();
if (this._sourceEditor) {
this._sourceEditor.off("dirty-change", this._onPropertyChange);
this._sourceEditor.off("save", this.saveToFile);
this._sourceEditor.off("change", this.updateStyleSheet);
this._sourceEditor.destroy();
}
this.cssSheet.off("property-change", this._onPropertyChange);
this.cssSheet.off("media-rules-changed", this._onMediaRulesChanged);

View File

@ -695,6 +695,7 @@ WebConsoleFrame.prototype = {
}, this);
aButton.setAttribute("checked", someChecked);
aButton.setAttribute("aria-pressed", someChecked);
}, this);
if (!this.owner._browserConsole) {
@ -834,12 +835,14 @@ WebConsoleFrame.prototype = {
Array.forEach(buttons, (button) => {
if (button !== target) {
button.setAttribute("checked", false);
button.setAttribute("aria-pressed", false);
this._setMenuState(button, false);
}
});
state = true;
}
target.setAttribute("checked", state);
target.setAttribute("aria-pressed", state);
// This is a filter button with a drop-down, and the user clicked the
// main part of the button. Go through all the severities and toggle
@ -888,6 +891,7 @@ WebConsoleFrame.prototype = {
}
let toolbarButton = menuPopup.parentNode;
toolbarButton.setAttribute("checked", someChecked);
toolbarButton.setAttribute("aria-pressed", someChecked);
break;
}
}

View File

@ -7,7 +7,7 @@ Cu.import("resource://gre/modules/Metrics.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource:///modules/experiments/Experiments.jsm");
Cu.import("resource://testing-common/services/healthreport/utils.jsm");
Cu.import("resource://testing-common/services-common/logging.js");
Cu.import("resource://testing-common/services/common/logging.js");
const kMeasurementVersion = 2;

View File

@ -83,6 +83,8 @@ _DEPRECATED_VARIABLES := \
MOCHITEST_METRO_FILES \
MOCHITEST_ROBOCOP_FILES \
SHORT_LIBNAME \
TESTING_JS_MODULES \
TESTING_JS_MODULE_DIR \
$(NULL)
ifndef EXTERNALLY_MANAGED_MAKE_FILE

View File

@ -1279,28 +1279,6 @@ PP_TARGETS += EXTRA_PP_JS_MODULES
endif
endif
################################################################################
# Copy testing-only JS modules to appropriate destination.
#
# For each file defined in TESTING_JS_MODULES, copy it to
# objdir/_tests/modules/. If TESTING_JS_MODULE_DIR is defined, that path
# wlll be appended to the output directory.
ifdef ENABLE_TESTS
ifdef TESTING_JS_MODULES
testmodulesdir = $(DEPTH)/_tests/modules/$(TESTING_JS_MODULE_DIR)
GENERATED_DIRS += $(testmodulesdir)
ifndef NO_DIST_INSTALL
TESTING_JS_MODULES_FILES := $(TESTING_JS_MODULES)
TESTING_JS_MODULES_DEST := $(testmodulesdir)
INSTALL_TARGETS += TESTING_JS_MODULES
endif
endif
endif
################################################################################
# SDK

View File

@ -277,16 +277,16 @@ public class GeckoView extends LayerView
String hint = message.optString("hint");
if ("alert".equals(hint)) {
String text = message.optString("text");
mChromeDelegate.onAlert(GeckoView.this, null, text, new PromptResult(message.optString("guid")));
mChromeDelegate.onAlert(GeckoView.this, null, text, new PromptResult(message));
} else if ("confirm".equals(hint)) {
String text = message.optString("text");
mChromeDelegate.onConfirm(GeckoView.this, null, text, new PromptResult(message.optString("guid")));
mChromeDelegate.onConfirm(GeckoView.this, null, text, new PromptResult(message));
} else if ("prompt".equals(hint)) {
String text = message.optString("text");
String defaultValue = message.optString("textbox0");
mChromeDelegate.onPrompt(GeckoView.this, null, text, defaultValue, new PromptResult(message.optString("guid")));
mChromeDelegate.onPrompt(GeckoView.this, null, text, defaultValue, new PromptResult(message));
} else if ("remotedebug".equals(hint)) {
mChromeDelegate.onDebugRequest(GeckoView.this, new PromptResult(message.optString("guid")));
mChromeDelegate.onDebugRequest(GeckoView.this, new PromptResult(message));
}
}
}
@ -439,16 +439,15 @@ public class GeckoView extends LayerView
private final int RESULT_OK = 0;
private final int RESULT_CANCEL = 1;
private final String mGUID;
private final JSONObject mMessage;
public PromptResult(String guid) {
mGUID = guid;
public PromptResult(JSONObject message) {
mMessage = message;
}
private JSONObject makeResult(int resultCode) {
JSONObject result = new JSONObject();
try {
result.put("guid", mGUID);
result.put("button", resultCode);
} catch(JSONException ex) { }
return result;
@ -459,7 +458,7 @@ public class GeckoView extends LayerView
*/
public void confirm() {
JSONObject result = makeResult(RESULT_OK);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Prompt:Reply", result.toString()));
EventDispatcher.sendResponse(mMessage, result);
}
/**
@ -471,7 +470,7 @@ public class GeckoView extends LayerView
try {
result.put("textbox0", value);
} catch(JSONException ex) { }
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Prompt:Reply", result.toString()));
EventDispatcher.sendResponse(mMessage, result);
}
/**
@ -479,7 +478,7 @@ public class GeckoView extends LayerView
*/
public void cancel() {
JSONObject result = makeResult(RESULT_CANCEL);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Prompt:Reply", result.toString()));
EventDispatcher.sendResponse(mMessage, result);
}
}

View File

@ -216,12 +216,8 @@ public class BrowserContract {
public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "combined");
public static final int DISPLAY_NORMAL = 0;
public static final int DISPLAY_READER = 1;
public static final String BOOKMARK_ID = "bookmark_id";
public static final String HISTORY_ID = "history_id";
public static final String DISPLAY = "display";
}
public static final class Schema {
@ -384,6 +380,10 @@ public class BrowserContract {
private Combined() {}
public static final String THUMBNAIL = "thumbnail";
public static final String DISPLAY = "display";
public static final int DISPLAY_NORMAL = 0;
public static final int DISPLAY_READER = 1;
}
static final String TABLE_BOOKMARKS_JOIN_IMAGES = Bookmarks.TABLE_NAME + " LEFT OUTER JOIN " +

View File

@ -34,7 +34,7 @@ import android.util.Log;
final class BrowserDatabaseHelper extends SQLiteOpenHelper {
private static final String LOGTAG = "GeckoBrowserDBHelper";
public static final int DATABASE_VERSION = 18;
public static final int DATABASE_VERSION = 19;
public static final String DATABASE_NAME = "browser.db";
final protected Context mContext;
@ -349,7 +349,7 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
Combined.URL + ", " +
Combined.TITLE + ", " +
Combined.VISITS + ", " +
Combined.DISPLAY + ", " +
Obsolete.Combined.DISPLAY + ", " +
Combined.DATE_LAST_VISITED + ", " +
qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " +
qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL +
@ -359,8 +359,8 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " +
Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " +
"-1 AS " + Combined.HISTORY_ID + ", " +
"-1 AS " + Combined.VISITS + ", " +
"-1 AS " + Combined.DATE_LAST_VISITED +
@ -378,8 +378,8 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
"COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " +
qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " +
Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " +
qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " +
qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " +
qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED +
@ -407,7 +407,7 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
Combined.URL + ", " +
Combined.TITLE + ", " +
Combined.VISITS + ", " +
Combined.DISPLAY + ", " +
Obsolete.Combined.DISPLAY + ", " +
Combined.DATE_LAST_VISITED + ", " +
qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " +
qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL +
@ -417,8 +417,8 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " +
Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " +
"-1 AS " + Combined.HISTORY_ID + ", " +
"-1 AS " + Combined.VISITS + ", " +
"-1 AS " + Combined.DATE_LAST_VISITED +
@ -437,8 +437,8 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
"COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " +
qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " +
Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " +
qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " +
qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " +
qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED +
@ -466,7 +466,7 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
Combined.URL + ", " +
Combined.TITLE + ", " +
Combined.VISITS + ", " +
Combined.DISPLAY + ", " +
Obsolete.Combined.DISPLAY + ", " +
Combined.DATE_LAST_VISITED + ", " +
qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " +
qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL +
@ -476,8 +476,8 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " +
Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " +
"-1 AS " + Combined.HISTORY_ID + ", " +
"-1 AS " + Combined.VISITS + ", " +
"-1 AS " + Combined.DATE_LAST_VISITED +
@ -495,12 +495,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
// customized the title for a bookmark.
"COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " +
qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " +
// Only use DISPLAY_READER if the matching bookmark entry inside reading
// list folder is not marked as deleted.
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID +
" THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " +
qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " +
qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED +
@ -528,7 +522,7 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
Combined.URL + ", " +
Combined.TITLE + ", " +
Combined.VISITS + ", " +
Combined.DISPLAY + ", " +
Obsolete.Combined.DISPLAY + ", " +
Combined.DATE_LAST_VISITED +
" FROM (" +
// Bookmarks without history.
@ -536,8 +530,8 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " +
Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " +
"-1 AS " + Combined.HISTORY_ID + ", " +
"-1 AS " + Combined.VISITS + ", " +
"-1 AS " + Combined.DATE_LAST_VISITED +
@ -551,16 +545,10 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
" SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " +
qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " +
// Prioritze bookmark titles over history titles, since the user may have
// Prioritize bookmark titles over history titles, since the user may have
// customized the title for a bookmark.
"COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " +
qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " +
// Only use DISPLAY_READER if the matching bookmark entry inside reading
// list folder is not marked as deleted.
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID +
" THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " +
qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " +
qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED +
@ -596,7 +584,7 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
Combined.URL + ", " +
Combined.TITLE + ", " +
Combined.VISITS + ", " +
Combined.DISPLAY + ", " +
Obsolete.Combined.DISPLAY + ", " +
Combined.DATE_LAST_VISITED + ", " +
Combined.FAVICON_ID +
" FROM (" +
@ -605,8 +593,8 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " +
Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " +
"-1 AS " + Combined.HISTORY_ID + ", " +
"-1 AS " + Combined.VISITS + ", " +
"-1 AS " + Combined.DATE_LAST_VISITED + ", " +
@ -629,8 +617,8 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
// list folder is not marked as deleted.
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID +
" THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
" THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " + Obsolete.Combined.DISPLAY_NORMAL + " END ELSE " +
Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " +
qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " +
qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " +
qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + ", " +
@ -653,73 +641,94 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
" ON " + Combined.FAVICON_ID + " = " + qualifyColumn(TABLE_FAVICONS, Favicons._ID));
}
private void createCombinedViewOn16(SQLiteDatabase db) {
debug("Creating " + VIEW_COMBINED + " view");
private void createCombinedViewOn19(SQLiteDatabase db) {
/*
The v19 combined view removes the redundant subquery from the v16
combined view and reorders the columns as necessary to prevent this
from breaking any code that might be referencing columns by index.
The rows in the ensuing view are, in order:
Combined.BOOKMARK_ID
Combined.HISTORY_ID
Combined._ID (always 0)
Combined.URL
Combined.TITLE
Combined.VISITS
Combined.DISPLAY
Combined.DATE_LAST_VISITED
Combined.FAVICON_ID
We need to return an _id column because CursorAdapter requires it for its
default implementation for the getItemId() method. However, since
we're not using this feature in the parts of the UI using this view,
we can just use 0 for all rows.
*/
db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" +
" SELECT " + Combined.BOOKMARK_ID + ", " +
Combined.HISTORY_ID + ", " +
// We need to return an _id column because CursorAdapter requires it for its
// default implementation for the getItemId() method. However, since
// we're not using this feature in the parts of the UI using this view,
// we can just use 0 for all rows.
"0 AS " + Combined._ID + ", " +
Combined.URL + ", " +
Combined.TITLE + ", " +
Combined.VISITS + ", " +
Combined.DISPLAY + ", " +
Combined.DATE_LAST_VISITED + ", " +
Combined.FAVICON_ID +
" FROM (" +
// Bookmarks without history.
" SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
"-1 AS " + Combined.HISTORY_ID + ", " +
"-1 AS " + Combined.VISITS + ", " +
"-1 AS " + Combined.DATE_LAST_VISITED + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.FAVICON_ID) + " AS " + Combined.FAVICON_ID +
" FROM " + TABLE_BOOKMARKS +
" WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " +
// Ignore pinned bookmarks.
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " <> " + Bookmarks.FIXED_PINNED_LIST_ID + " AND " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) +
" NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" +
" UNION ALL" +
// Bookmarks without history.
" SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + "," +
"-1 AS " + Combined.HISTORY_ID + "," +
"0 AS " + Combined._ID + "," +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " +
"-1 AS " + Combined.VISITS + ", " +
"-1 AS " + Combined.DATE_LAST_VISITED + "," +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.FAVICON_ID) + " AS " + Combined.FAVICON_ID +
" FROM " + TABLE_BOOKMARKS +
" WHERE " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " +
// Ignore pinned bookmarks.
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " <> " + Bookmarks.FIXED_PINNED_LIST_ID + " AND " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) +
" NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" +
" UNION ALL" +
// History with and without bookmark.
" SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " +
// Give pinned bookmarks a NULL ID so that they're not treated as bookmarks. We can't
// completely ignore them here because they're joined with history entries we care about.
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
Bookmarks.FIXED_PINNED_LIST_ID + " THEN NULL ELSE " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " END " +
"ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " +
qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " +
// Prioritize bookmark titles over history titles, since the user may have
// customized the title for a bookmark.
"COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " +
qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " +
// Only use DISPLAY_READER if the matching bookmark entry inside reading
// list folder is not marked as deleted.
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID +
" THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " +
qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " +
qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + ", " +
qualifyColumn(TABLE_HISTORY, History.FAVICON_ID) + " AS " + Combined.FAVICON_ID +
" SELECT " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) +
// Give pinned bookmarks a NULL ID so that they're not treated as bookmarks. We can't
// completely ignore them here because they're joined with history entries we care about.
" WHEN 0 THEN " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) +
" WHEN " + Bookmarks.FIXED_PINNED_LIST_ID + " THEN " +
"NULL " +
"ELSE " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) +
" END " +
"ELSE " +
"NULL " +
"END AS " + Combined.BOOKMARK_ID + "," +
qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + "," +
"0 AS " + Combined._ID + "," +
qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + "," +
// Prioritize bookmark titles over history titles, since the user may have
// customized the title for a bookmark.
"COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " +
qualifyColumn(TABLE_HISTORY, History.TITLE) +
") AS " + Combined.TITLE + "," +
qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + "," +
qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + "," +
qualifyColumn(TABLE_HISTORY, History.FAVICON_ID) + " AS " + Combined.FAVICON_ID +
// We really shouldn't be selecting deleted bookmarks, but oh well.
" FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS +
" ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) +
" WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " +
qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " +
")");
" ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) +
" WHERE " +
qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND " +
"(" +
// The left outer join didn't match...
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " +
// ... or it's a bookmark. This is less efficient than filtering prior
// to the join if you have lots of folders.
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK +
")"
);
debug("Creating " + VIEW_COMBINED_WITH_FAVICONS + " view");
@ -742,7 +751,7 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
createBookmarksWithFaviconsView(db);
createHistoryWithFaviconsView(db);
createCombinedViewOn16(db);
createCombinedViewOn19(db);
createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID,
R.string.bookmarks_folder_places, 0);
@ -1298,10 +1307,10 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
}
private void upgradeDatabaseFrom15to16(SQLiteDatabase db) {
db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED);
db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED_WITH_FAVICONS);
createCombinedViewOn16(db);
// No harm in creating the v19 combined view here: means we don't need two almost-identical
// functions to define both the v16 and v19 ones. The upgrade path will redundantly drop
// and recreate the view again. *shrug*
createV19CombinedView(db);
}
private void upgradeDatabaseFrom16to17(SQLiteDatabase db) {
@ -1378,6 +1387,26 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
}
}
private void upgradeDatabaseFrom18to19(SQLiteDatabase db) {
// Redefine the "combined" view...
createV19CombinedView(db);
// Kill any history entries with NULL URL. This ostensibly can't happen...
db.execSQL("DELETE FROM " + TABLE_HISTORY + " WHERE " + History.URL + " IS NULL");
// Similar for bookmark types. Replaces logic from the combined view, also shouldn't happen.
db.execSQL("UPDATE " + TABLE_BOOKMARKS + " SET " +
Bookmarks.TYPE + " = " + Bookmarks.TYPE_BOOKMARK +
" WHERE " + Bookmarks.TYPE + " IS NULL");
}
private void createV19CombinedView(SQLiteDatabase db) {
db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED);
db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED_WITH_FAVICONS);
createCombinedViewOn19(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
debug("Upgrading browser.db: " + db.getPath() + " from " +
@ -1454,6 +1483,10 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
case 18:
upgradeDatabaseFrom17to18(db);
break;
case 19:
upgradeDatabaseFrom18to19(db);
break;
}
}
@ -1566,4 +1599,4 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
bookmark.remove("folder");
}
}
}
}

View File

@ -195,7 +195,6 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
map.put(Combined._ID, Combined._ID);
map.put(Combined.BOOKMARK_ID, Combined.BOOKMARK_ID);
map.put(Combined.HISTORY_ID, Combined.HISTORY_ID);
map.put(Combined.DISPLAY, "MAX(" + Combined.DISPLAY + ") AS " + Combined.DISPLAY);
map.put(Combined.URL, Combined.URL);
map.put(Combined.TITLE, Combined.TITLE);
map.put(Combined.VISITS, Combined.VISITS);

View File

@ -526,7 +526,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
new String[] { Combined._ID,
Combined.URL,
Combined.TITLE,
Combined.DISPLAY,
Combined.BOOKMARK_ID,
Combined.HISTORY_ID },
constraint,
@ -550,7 +549,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
new String[] { Combined._ID,
Combined.URL,
Combined.TITLE,
Combined.DISPLAY,
Combined.BOOKMARK_ID,
Combined.HISTORY_ID },
"",
@ -639,7 +637,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
Combined.HISTORY_ID,
Combined.URL,
Combined.TITLE,
Combined.DISPLAY,
Combined.DATE_LAST_VISITED,
Combined.VISITS },
History.DATE_LAST_VISITED + " > 0",
@ -783,16 +780,13 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
@Override
public boolean isBookmark(ContentResolver cr, String uri) {
// This method is about normal bookmarks, not the Reading List.
Cursor c = null;
try {
c = cr.query(bookmarksUriWithLimit(1),
new String[] { Bookmarks._ID },
Bookmarks.URL + " = ? AND " +
Bookmarks.PARENT + " != ? AND " +
Bookmarks.PARENT + " != ?",
new String[] { uri,
String.valueOf(Bookmarks.FIXED_READING_LIST_ID),
String.valueOf(Bookmarks.FIXED_PINNED_LIST_ID) },
Bookmarks.URL);
return c.getCount() > 0;
@ -989,9 +983,8 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
// Do this now so that the items still exist!
bumpParents(cr, Bookmarks.URL, uri);
// Toggling bookmark on an URL should not affect the items in the reading list or pinned sites.
final String[] urlArgs = new String[] { uri, String.valueOf(Bookmarks.FIXED_READING_LIST_ID), String.valueOf(Bookmarks.FIXED_PINNED_LIST_ID) };
final String urlEquals = Bookmarks.URL + " = ? AND " + Bookmarks.PARENT + " != ? AND " + Bookmarks.PARENT + " != ? ";
final String[] urlArgs = new String[] { uri, String.valueOf(Bookmarks.FIXED_PINNED_LIST_ID) };
final String urlEquals = Bookmarks.URL + " = ? AND " + Bookmarks.PARENT + " != ? ";
cr.delete(contentUri, urlEquals, urlArgs);
}
@ -1346,32 +1339,26 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
if (url != null) {
// Bookmarks are defined by their URL and Folder.
builder.withSelection(Bookmarks.URL + " = ? AND "
+ Bookmarks.PARENT + " = ? AND "
+ Bookmarks.PARENT + " != ?",
+ Bookmarks.PARENT + " = ?",
new String[] { url,
Long.toString(parent),
String.valueOf(Bookmarks.FIXED_READING_LIST_ID)
Long.toString(parent)
});
} else if (title != null) {
// Or their title and parent folder. (Folders!)
builder.withSelection(Bookmarks.TITLE + " = ? AND "
+ Bookmarks.PARENT + " = ? AND "
+ Bookmarks.PARENT + " != ?",
+ Bookmarks.PARENT + " = ?",
new String[] { title,
Long.toString(parent),
String.valueOf(Bookmarks.FIXED_READING_LIST_ID)
Long.toString(parent)
});
} else if (type == Bookmarks.TYPE_SEPARATOR) {
// Or their their position (seperators)
// Or their their position (separators)
builder.withSelection(Bookmarks.POSITION + " = ? AND "
+ Bookmarks.PARENT + " = ? AND "
+ Bookmarks.PARENT + " != ?",
+ Bookmarks.PARENT + " = ?",
new String[] { Long.toString(position),
Long.toString(parent),
String.valueOf(Bookmarks.FIXED_READING_LIST_ID)
Long.toString(parent)
});
} else {
Log.e(LOGTAG, "Bookmark entry without url or title and not a seperator, not added.");
Log.e(LOGTAG, "Bookmark entry without url or title and not a separator, not added.");
}
builder.withValues(values);

View File

@ -111,7 +111,6 @@ public class HistoryPanel extends HomeFragment {
final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
info.url = cursor.getString(cursor.getColumnIndexOrThrow(Combined.URL));
info.title = cursor.getString(cursor.getColumnIndexOrThrow(Combined.TITLE));
info.display = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.DISPLAY));
info.historyId = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.HISTORY_ID));
final int bookmarkIdCol = cursor.getColumnIndexOrThrow(Combined.BOOKMARK_ID);
if (cursor.isNull(bookmarkIdCol)) {

View File

@ -21,7 +21,6 @@ public class HomeContextMenuInfo extends AdapterContextMenuInfo {
public String url;
public String title;
public boolean isFolder = false;
public int display = Combined.DISPLAY_NORMAL;
public int historyId = -1;
public int bookmarkId = -1;
public int readingListItemId = -1;

View File

@ -98,7 +98,7 @@ abstract class HomeFragment extends Fragment {
menu.setHeaderTitle(info.getDisplayTitle());
// Hide ununsed menu items.
// Hide unused menu items.
menu.findItem(R.id.top_sites_edit).setVisible(false);
menu.findItem(R.id.top_sites_pin).setVisible(false);
menu.findItem(R.id.top_sites_unpin).setVisible(false);
@ -117,9 +117,6 @@ abstract class HomeFragment extends Fragment {
if (!StringUtils.isShareableUrl(info.url) || GeckoProfile.get(getActivity()).inGuestMode()) {
menu.findItem(R.id.home_share).setVisible(false);
}
final boolean canOpenInReader = (info.display == Combined.DISPLAY_READER);
menu.findItem(R.id.home_open_in_reader).setVisible(canOpenInReader);
}
@Override
@ -226,12 +223,6 @@ abstract class HomeFragment extends Fragment {
return true;
}
if (itemId == R.id.home_open_in_reader) {
final String url = ReaderModeUtils.getAboutReaderForUrl(info.url);
Tabs.getInstance().loadUrl(url, Tabs.LOADURL_NONE);
return true;
}
if (itemId == R.id.home_remove) {
if (info instanceof TopSitesGridContextMenuInfo) {
(new RemoveItemByUrlTask(context, info.url, info.position)).execute();

View File

@ -279,8 +279,7 @@ public class TopSitesPanel extends HomeFragment {
MenuInflater inflater = new MenuInflater(view.getContext());
inflater.inflate(R.menu.home_contextmenu, menu);
// Hide ununsed menu items.
menu.findItem(R.id.home_open_in_reader).setVisible(false);
// Hide unused menu items.
menu.findItem(R.id.home_edit_bookmark).setVisible(false);
TopSitesGridContextMenuInfo info = (TopSitesGridContextMenuInfo) menuInfo;

View File

@ -197,21 +197,11 @@ public class TwoLinePageRow extends LinearLayout
final int bookmarkIdIndex = cursor.getColumnIndex(Combined.BOOKMARK_ID);
if (bookmarkIdIndex != -1) {
final long bookmarkId = cursor.getLong(bookmarkIdIndex);
final int displayIndex = cursor.getColumnIndex(Combined.DISPLAY);
final int display;
if (displayIndex != -1) {
display = cursor.getInt(displayIndex);
} else {
display = Combined.DISPLAY_NORMAL;
}
// The bookmark id will be 0 (null in database) when the url
// is not a bookmark.
if (bookmarkId == 0) {
setPageTypeIcon(NO_ICON);
} else if (display == Combined.DISPLAY_READER) {
setPageTypeIcon(R.drawable.ic_url_bar_reader);
} else {
setPageTypeIcon(R.drawable.ic_url_bar_star);
}

View File

@ -280,7 +280,6 @@ size. -->
<!ENTITY contextmenu_open_new_tab "Open in New Tab">
<!ENTITY contextmenu_open_private_tab "Open in Private Tab">
<!ENTITY contextmenu_open_in_reader "Open in Reader">
<!ENTITY contextmenu_remove "Remove">
<!ENTITY contextmenu_add_to_launcher "Add to Home Screen">
<!ENTITY contextmenu_share "Share">

View File

@ -5,7 +5,6 @@
package org.mozilla.gecko.preferences;
import java.lang.reflect.Field;
import java.util.Locale;
import org.mozilla.gecko.BrowserLocaleManager;
@ -27,7 +26,6 @@ import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.ViewConfiguration;
/* A simple implementation of PreferenceFragment for large screen devices
* This will strip category headers (so that they aren't shown to the user twice)
@ -198,29 +196,4 @@ public class GeckoPreferenceFragment extends PreferenceFragment {
PrefsHelper.removeObserver(mPrefsRequestId);
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
showOverflowMenu(activity);
}
/*
* Force the overflow 3-dot menu to be displayed if it isn't already displayed.
*
* This is an ugly hack for 4.0+ Android devices that don't have a dedicated menu button
* because Android does not provide a public API to display the ActionBar overflow menu.
*/
private void showOverflowMenu(Activity activity) {
try {
ViewConfiguration config = ViewConfiguration.get(activity);
Field menuOverflow = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
if (menuOverflow != null) {
menuOverflow.setAccessible(true);
menuOverflow.setBoolean(config, false);
}
} catch (Exception e) {
Log.d(LOGTAG, "Failed to force overflow menu, ignoring.");
}
}
}
}

View File

@ -4,8 +4,8 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/restore_defaults"
android:title="@string/pref_search_restore_defaults" />
<item android:id="@+id/restore_defaults"
android:drawable="@drawable/menu"
android:showAsAction="never"
android:title="@string/pref_search_restore_defaults" />
</menu>

View File

@ -11,9 +11,6 @@
<item android:id="@+id/home_open_private_tab"
android:title="@string/contextmenu_open_private_tab"/>
<item android:id="@+id/home_open_in_reader"
android:title="@string/contextmenu_open_in_reader"/>
<item android:id="@+id/home_copyurl"
android:title="@string/contextmenu_copyurl"/>

View File

@ -274,7 +274,6 @@
<string name="contextmenu_open_new_tab">&contextmenu_open_new_tab;</string>
<string name="contextmenu_open_private_tab">&contextmenu_open_private_tab;</string>
<string name="contextmenu_open_in_reader">&contextmenu_open_in_reader;</string>
<string name="contextmenu_remove">&contextmenu_remove;</string>
<string name="contextmenu_add_to_launcher">&contextmenu_add_to_launcher;</string>
<string name="contextmenu_share">&contextmenu_share;</string>

View File

@ -38,8 +38,15 @@ class RemoteTabsList extends ExpandableListView
private final Context context;
private TabsPanel tabsPanel;
private ArrayList <HashMap <String, String>> clients;
private ArrayList <ArrayList <HashMap <String, String>>> tabsList;
// A list of the clients that are currently expanded.
private List<String> expandedClientList = new ArrayList<String>();
// The client that previously had an item selected is used to restore the scroll position.
private String clientScrollPosition;
public RemoteTabsList(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
@ -57,8 +64,18 @@ class RemoteTabsList extends ExpandableListView
}
@Override
public boolean onGroupClick(ExpandableListView parent, View view, int position, long id) {
// By default, the group collapses/expands. Consume the event.
public boolean onGroupClick(ExpandableListView parent, View view, int groupPosition, long id) {
final String clientGuid = clients.get(groupPosition).get("guid");
if (isGroupExpanded(groupPosition)) {
collapseGroup(groupPosition);
expandedClientList.remove(clientGuid);
} else {
expandGroup(groupPosition);
expandedClientList.add(clientGuid);
}
clientScrollPosition = clientGuid;
return true;
}
@ -74,6 +91,8 @@ class RemoteTabsList extends ExpandableListView
Tabs.getInstance().loadUrl(tab.get("url"), Tabs.LOADURL_NEW_TAB);
autoHidePanel();
clientScrollPosition = clients.get(groupPosition).get("guid");
return true;
}
@ -83,8 +102,7 @@ class RemoteTabsList extends ExpandableListView
if (remoteTabs == null || remoteTabs.size() == 0)
return;
ArrayList <HashMap <String, String>> clients = new ArrayList <HashMap <String, String>>();
clients = new ArrayList <HashMap <String, String>>();
tabsList = new ArrayList <ArrayList <HashMap <String, String>>>();
String oldGuid = null;
@ -95,16 +113,18 @@ class RemoteTabsList extends ExpandableListView
final long now = System.currentTimeMillis();
for (TabsAccessor.RemoteTab remoteTab : remoteTabs) {
if (oldGuid == null || !TextUtils.equals(oldGuid, remoteTab.guid)) {
final String clientGuid = remoteTab.guid;
if (oldGuid == null || !TextUtils.equals(oldGuid, clientGuid)) {
client = new HashMap <String, String>();
client.put("name", remoteTab.name);
client.put("last_synced", getLastSyncedString(now, remoteTab.lastModified));
client.put("guid", clientGuid);
clients.add(client);
tabsForClient = new ArrayList <HashMap <String, String>>();
tabsList.add(tabsForClient);
oldGuid = new String(remoteTab.guid);
oldGuid = new String(clientGuid);
}
tab = new HashMap<String, String>();
@ -123,9 +143,20 @@ class RemoteTabsList extends ExpandableListView
TAB_KEY,
TAB_RESOURCE));
// Expand the client groups, and restore the previous scroll position.
List<String> newExpandedClientList = new ArrayList<String>();
for (int i = 0; i < clients.size(); i++) {
expandGroup(i);
final String clientGuid = clients.get(i).get("guid");
if (expandedClientList.contains(clientGuid)) {
newExpandedClientList.add(clientGuid);
expandGroup(i);
}
if (clientGuid.equals(clientScrollPosition)) {
setSelectedGroup(i);
}
}
expandedClientList = newExpandedClientList;
}
/**

View File

@ -3,6 +3,7 @@ package org.mozilla.gecko.tests;
import java.util.ArrayList;
import java.util.Random;
import org.mozilla.gecko.background.db.CursorDumper;
import org.mozilla.gecko.db.BrowserContract;
import android.content.ContentProviderOperation;
@ -251,7 +252,6 @@ public class testBrowserProvider extends ContentProviderTest {
mTests.add(new TestCombinedView());
mTests.add(new TestCombinedViewDisplay());
mTests.add(new TestCombinedViewWithDeletedBookmark());
mTests.add(new TestCombinedViewWithDeletedReadingListItem());
mTests.add(new TestExpireHistory());
mTests.add(new TestBrowserProviderNotifications());
@ -454,21 +454,18 @@ public class testBrowserProvider extends ContentProviderTest {
BrowserContract.Bookmarks.GUID + " = ? OR " +
BrowserContract.Bookmarks.GUID + " = ? OR " +
BrowserContract.Bookmarks.GUID + " = ? OR " +
BrowserContract.Bookmarks.GUID + " = ? OR " +
BrowserContract.Bookmarks.GUID + " = ?",
new String[] { BrowserContract.Bookmarks.PLACES_FOLDER_GUID,
BrowserContract.Bookmarks.MOBILE_FOLDER_GUID,
BrowserContract.Bookmarks.MENU_FOLDER_GUID,
BrowserContract.Bookmarks.TAGS_FOLDER_GUID,
BrowserContract.Bookmarks.TOOLBAR_FOLDER_GUID,
BrowserContract.Bookmarks.UNFILED_FOLDER_GUID,
BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID },
BrowserContract.Bookmarks.UNFILED_FOLDER_GUID},
null);
mAsserter.is(c.getCount(), 7, "Right number of special folders");
mAsserter.is(c.getCount(), 6, "Right number of special folders");
int rootId = BrowserContract.Bookmarks.FIXED_ROOT_ID;
int readingListId = BrowserContract.Bookmarks.FIXED_READING_LIST_ID;
while (c.moveToNext()) {
int id = c.getInt(c.getColumnIndex(BrowserContract.Bookmarks._ID));
@ -476,12 +473,10 @@ public class testBrowserProvider extends ContentProviderTest {
int parentId = c.getInt(c.getColumnIndex(BrowserContract.Bookmarks.PARENT));
if (guid.equals(BrowserContract.Bookmarks.PLACES_FOLDER_GUID)) {
mAsserter.is(new Integer(id), new Integer(rootId), "The id of places folder is correct");
} else if (guid.equals(BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID)) {
mAsserter.is(new Integer(id), new Integer(readingListId), "The id of reading list folder is correct");
mAsserter.is(id, rootId, "The id of places folder is correct");
}
mAsserter.is(new Integer(parentId), new Integer(rootId),
mAsserter.is(parentId, rootId,
"The PARENT of the " + guid + " special folder is correct");
}
@ -506,61 +501,66 @@ public class testBrowserProvider extends ContentProviderTest {
public void test() throws Exception {
ContentValues b = createOneBookmark();
long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
Cursor c = getBookmarkById(id);
mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
final Cursor c = getBookmarkById(id);
try {
mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TITLE)), b.getAsString(BrowserContract.Bookmarks.TITLE),
"Inserted bookmark has correct title");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), b.getAsString(BrowserContract.Bookmarks.URL),
"Inserted bookmark has correct URL");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TAGS)), b.getAsString(BrowserContract.Bookmarks.TAGS),
"Inserted bookmark has correct tags");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.KEYWORD)), b.getAsString(BrowserContract.Bookmarks.KEYWORD),
"Inserted bookmark has correct keyword");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.DESCRIPTION)), b.getAsString(BrowserContract.Bookmarks.DESCRIPTION),
"Inserted bookmark has correct description");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.POSITION)), b.getAsString(BrowserContract.Bookmarks.POSITION),
"Inserted bookmark has correct position");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), b.getAsString(BrowserContract.Bookmarks.TYPE),
"Inserted bookmark has correct type");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.PARENT)), b.getAsString(BrowserContract.Bookmarks.PARENT),
"Inserted bookmark has correct parent ID");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.IS_DELETED)), String.valueOf(0),
"Inserted bookmark has correct is-deleted state");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TITLE)), b.getAsString(BrowserContract.Bookmarks.TITLE),
"Inserted bookmark has correct title");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), b.getAsString(BrowserContract.Bookmarks.URL),
"Inserted bookmark has correct URL");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TAGS)), b.getAsString(BrowserContract.Bookmarks.TAGS),
"Inserted bookmark has correct tags");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.KEYWORD)), b.getAsString(BrowserContract.Bookmarks.KEYWORD),
"Inserted bookmark has correct keyword");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.DESCRIPTION)), b.getAsString(BrowserContract.Bookmarks.DESCRIPTION),
"Inserted bookmark has correct description");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.POSITION)), b.getAsString(BrowserContract.Bookmarks.POSITION),
"Inserted bookmark has correct position");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), b.getAsString(BrowserContract.Bookmarks.TYPE),
"Inserted bookmark has correct type");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.PARENT)), b.getAsString(BrowserContract.Bookmarks.PARENT),
"Inserted bookmark has correct parent ID");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.IS_DELETED)), String.valueOf(0),
"Inserted bookmark has correct is-deleted state");
id = insertWithNullCol(BrowserContract.Bookmarks.POSITION);
mAsserter.is(new Long(id), new Long(-1),
"Should not be able to insert bookmark with null position");
id = insertWithNullCol(BrowserContract.Bookmarks.POSITION);
mAsserter.is(id, -1L,
"Should not be able to insert bookmark with null position");
id = insertWithNullCol(BrowserContract.Bookmarks.TYPE);
mAsserter.is(new Long(id), new Long(-1),
"Should not be able to insert bookmark with null type");
id = insertWithNullCol(BrowserContract.Bookmarks.TYPE);
mAsserter.is(id, -1L,
"Should not be able to insert bookmark with null type");
if (Build.VERSION.SDK_INT >= 8 &&
Build.VERSION.SDK_INT < 16) {
b = createOneBookmark();
b.put(BrowserContract.Bookmarks.PARENT, -1);
id = -1;
try {
id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
} catch (Exception e) {}
mAsserter.is(id, -1L,
"Should not be able to insert bookmark with invalid parent");
}
if (Build.VERSION.SDK_INT >= 8 &&
Build.VERSION.SDK_INT < 16) {
b = createOneBookmark();
b.put(BrowserContract.Bookmarks.PARENT, -1);
id = -1;
b.remove(BrowserContract.Bookmarks.TYPE);
id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
final Cursor c2 = getBookmarkById(id);
try {
id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
} catch (Exception e) {}
mAsserter.is(new Long(id), new Long(-1),
"Should not be able to insert bookmark with invalid parent");
mAsserter.is(c2.moveToFirst(), true, "Inserted bookmark found");
mAsserter.is(c2.getString(c2.getColumnIndex(BrowserContract.Bookmarks.TYPE)), String.valueOf(BrowserContract.Bookmarks.TYPE_BOOKMARK),
"Inserted bookmark has correct default type");
} finally {
c2.close();
}
} finally {
c.close();
}
b = createOneBookmark();
b.remove(BrowserContract.Bookmarks.TYPE);
id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
c = getBookmarkById(id);
mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), String.valueOf(BrowserContract.Bookmarks.TYPE_BOOKMARK),
"Inserted bookmark has correct default type");
c.close();
}
}
@ -755,12 +755,12 @@ public class testBrowserProvider extends ContentProviderTest {
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), u.getAsString(BrowserContract.Bookmarks.TYPE),
"Inserted bookmark has correct type");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_CREATED))),
new Long(dateCreated),
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_CREATED)),
dateCreated,
"Updated bookmark has same creation date");
mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_MODIFIED))),
new Long(dateModified),
mAsserter.isnot(c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_MODIFIED)),
dateModified,
"Updated bookmark has new modification date");
updated = updateWithNullCol(id, BrowserContract.Bookmarks.POSITION);
@ -948,11 +948,11 @@ public class testBrowserProvider extends ContentProviderTest {
"Inserted history entry has correct is-deleted state");
id = insertWithNullCol(BrowserContract.History.URL);
mAsserter.is(new Long(id), new Long(-1),
mAsserter.is(id, -1L,
"Should not be able to insert history with null URL");
id = insertWithNullCol(BrowserContract.History.VISITS);
mAsserter.is(new Long(id), new Long(-1),
mAsserter.is(id, -1L,
"Should not be able to insert history with null number of visits");
c.close();
}
@ -1112,12 +1112,12 @@ public class testBrowserProvider extends ContentProviderTest {
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.DATE_LAST_VISITED)), u.getAsString(BrowserContract.History.DATE_LAST_VISITED),
"Updated history entry has correct last visited date");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED))),
new Long(dateCreated),
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED)),
dateCreated,
"Updated history entry has same creation date");
mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED))),
new Long(dateModified),
mAsserter.isnot(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED)),
dateModified,
"Updated history entry has new modification date");
updated = updateWithNullCol(id, BrowserContract.History.URL);
@ -1228,7 +1228,7 @@ public class testBrowserProvider extends ContentProviderTest {
long dateCreated = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED));
long dateModified = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED));
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(1),
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS)), 1L,
"Inserted history entry has correct default number of visits");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_URL_1,
"Inserted history entry has correct default title");
@ -1249,11 +1249,11 @@ public class testBrowserProvider extends ContentProviderTest {
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE,
"Updated history entry has correct title");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(2),
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS)), 2L,
"Updated history entry has correct number of visits");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED))), new Long(dateCreated),
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED)), dateCreated,
"Updated history entry has same creation date");
mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED))), new Long(dateModified),
mAsserter.isnot(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED)), dateModified,
"Updated history entry has new modification date");
// Create a new history entry, specifying visits and history
@ -1276,7 +1276,7 @@ public class testBrowserProvider extends ContentProviderTest {
dateCreated = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED));
dateModified = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED));
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(10),
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS)), 10L,
"Inserted history entry has correct specified number of visits");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE,
"Inserted history entry has correct specified title");
@ -1298,11 +1298,11 @@ public class testBrowserProvider extends ContentProviderTest {
"Updated history entry has correct unchanged title");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), TEST_URL_2,
"Updated history entry has correct unchanged URL");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(20),
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS)), 20L,
"Updated history entry has correct number of visits");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED))), new Long(dateCreated),
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED)), dateCreated,
"Updated history entry has same creation date");
mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED))), new Long(dateModified),
mAsserter.isnot(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED)), dateModified,
"Updated history entry has new modification date");
c.close();
@ -1433,66 +1433,69 @@ public class testBrowserProvider extends ContentProviderTest {
mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, folderBookmark);
// Sort entries by url so we can check them individually
Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, BrowserContract.Combined.URL);
final Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, BrowserContract.Combined.URL);
mAsserter.is(c.getCount(), 3, "3 combined entries found");
// First combined entry is basic history entry
mAsserter.is(c.moveToFirst(), true, "Found basic history entry");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID))), new Long(0),
"Combined _id column should always be 0");
// TODO: Should we change BrowserProvider to make this return -1, not 0?
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
"Bookmark id should be 0 for basic history entry");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID))), new Long(basicHistoryId),
"Basic history entry has correct history id");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)), TITLE_1,
"Basic history entry has correct title");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_1,
"Basic history entry has correct url");
mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), VISITS,
"Basic history entry has correct number of visits");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED))), new Long(LAST_VISITED),
"Basic history entry has correct last visit time");
// Second combined entry is basic bookmark entry
mAsserter.is(c.moveToNext(), true, "Found basic bookmark entry");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID))), new Long(0),
"Combined _id column should always be 0");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(basicBookmarkId),
"Basic bookmark entry has correct bookmark id");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID))), new Long(-1),
"History id should be -1 for basic bookmark entry");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)), TITLE_2,
"Basic bookmark entry has correct title");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_2,
"Basic bookmark entry has correct url");
mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), -1,
"Visits should be -1 for basic bookmark entry");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED))), new Long(-1),
"Basic entry has correct last visit time");
// Third combined entry is a combined history/bookmark entry
mAsserter.is(c.moveToNext(), true, "Found third combined entry");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID))), new Long(0),
"Combined _id column should always be 0");
// The bookmark data (bookmark_id and title) associated with the combined entry is non-deterministic,
// it might end up with data coming from any of the matching bookmark entries.
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)) == combinedBookmarkId ||
c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)) == combinedBookmarkId2, true,
"Combined entry has correct bookmark id");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)).equals(TITLE_3_BOOKMARK) ||
c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)).equals(TITLE_3_BOOKMARK2), true,
"Combined entry has title corresponding to bookmark entry");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID))), new Long(combinedHistoryId),
"Combined entry has correct history id");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_3,
"Combined entry has correct url");
mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), VISITS,
"Combined entry has correct number of visits");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED))), new Long(LAST_VISITED),
"Combined entry has correct last visit time");
c.close();
try {
mAsserter.is(c.getCount(), 3, "3 combined entries found");
// First combined entry is basic history entry
mAsserter.is(c.moveToFirst(), true, "Found basic history entry");
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID)), 0L,
"Combined _id column should always be 0");
// TODO: Should we change BrowserProvider to make this return -1, not 0?
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)), 0L,
"Bookmark id should be 0 for basic history entry");
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID)), basicHistoryId,
"Basic history entry has correct history id");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)), TITLE_1,
"Basic history entry has correct title");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_1,
"Basic history entry has correct url");
mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), VISITS,
"Basic history entry has correct number of visits");
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED)), LAST_VISITED,
"Basic history entry has correct last visit time");
// Second combined entry is basic bookmark entry
mAsserter.is(c.moveToNext(), true, "Found basic bookmark entry");
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID)), 0L,
"Combined _id column should always be 0");
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)), basicBookmarkId,
"Basic bookmark entry has correct bookmark id");
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID)), -1L,
"History id should be -1 for basic bookmark entry");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)), TITLE_2,
"Basic bookmark entry has correct title");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_2,
"Basic bookmark entry has correct url");
mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), -1,
"Visits should be -1 for basic bookmark entry");
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED)), -1L,
"Basic entry has correct last visit time");
// Third combined entry is a combined history/bookmark entry
mAsserter.is(c.moveToNext(), true, "Found third combined entry");
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID)), 0L,
"Combined _id column should always be 0");
// The bookmark data (bookmark_id and title) associated with the combined entry is non-deterministic,
// it might end up with data coming from any of the matching bookmark entries.
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)) == combinedBookmarkId ||
c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)) == combinedBookmarkId2, true,
"Combined entry has correct bookmark id");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)).equals(TITLE_3_BOOKMARK) ||
c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)).equals(TITLE_3_BOOKMARK2), true,
"Combined entry has title corresponding to bookmark entry");
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID)), combinedHistoryId,
"Combined entry has correct history id");
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_3,
"Combined entry has correct url");
mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), VISITS,
"Combined entry has correct number of visits");
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED)), LAST_VISITED,
"Combined entry has correct last visit time");
} finally {
c.close();
}
}
}
@ -1503,12 +1506,10 @@ public class testBrowserProvider extends ContentProviderTest {
final String TITLE_2 = "Test Page 2";
final String TITLE_3_HISTORY = "Test Page 3 (History Entry)";
final String TITLE_3_BOOKMARK = "Test Page 3 (Bookmark Entry)";
final String TITLE_4 = "Test Page 4";
final String URL_1 = "http://example.com";
final String URL_2 = "http://example.org";
final String URL_3 = "http://examples2.com";
final String URL_4 = "http://readinglist.com";
final int VISITS = 10;
final long LAST_VISITED = System.currentTimeMillis();
@ -1531,29 +1532,12 @@ public class testBrowserProvider extends ContentProviderTest {
BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedBookmark);
// Create a reading list entries
int readingListId = BrowserContract.Bookmarks.FIXED_READING_LIST_ID;
ContentValues readingListItem = createBookmark(TITLE_3_BOOKMARK, URL_3, readingListId,
BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
long readingListItemId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, readingListItem));
ContentValues readingListItem2 = createBookmark(TITLE_4, URL_4, readingListId,
BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
long readingListItemId2 = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, readingListItem2));
Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
mAsserter.is(c.getCount(), 4, "4 combined entries found");
while (c.moveToNext()) {
long id = c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID));
int display = c.getInt(c.getColumnIndex(BrowserContract.Combined.DISPLAY));
int expectedDisplay = (id == readingListItemId || id == readingListItemId2 ? BrowserContract.Combined.DISPLAY_READER : BrowserContract.Combined.DISPLAY_NORMAL);
mAsserter.is(new Integer(display), new Integer(expectedDisplay),
"Combined display column should always be DISPLAY_READER for the reading list item");
final Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
try {
mAsserter.is(c.getCount(), 3, "3 combined entries found");
} finally {
c.close();
}
c.close();
}
}
@ -1578,7 +1562,7 @@ public class testBrowserProvider extends ContentProviderTest {
mAsserter.is(c.getCount(), 1, "1 combined entry found");
mAsserter.is(c.moveToFirst(), true, "Found combined entry with bookmark id");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(combinedBookmarkId),
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)), combinedBookmarkId,
"Bookmark id should be set correctly on combined entry");
int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
@ -1592,58 +1576,12 @@ public class testBrowserProvider extends ContentProviderTest {
mAsserter.is(c.getCount(), 1, "1 combined entry found");
mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)), 0L,
"Bookmark id should not be set to removed bookmark id");
c.close();
}
}
private class TestCombinedViewWithDeletedReadingListItem extends TestCase {
@Override
public void test() throws Exception {
final String TITLE = "Test Page 1";
final String URL = "http://example.com";
final int VISITS = 10;
final long LAST_VISITED = System.currentTimeMillis();
// Create a combined history entry
ContentValues combinedHistory = createHistoryEntry(TITLE, URL, VISITS, LAST_VISITED);
mProvider.insert(BrowserContract.History.CONTENT_URI, combinedHistory);
// Create a combined bookmark entry
int readingListId = BrowserContract.Bookmarks.FIXED_READING_LIST_ID;
ContentValues combinedReadingListItem = createBookmark(TITLE, URL, readingListId,
BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
long combinedReadingListItemId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedReadingListItem));
Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
mAsserter.is(c.getCount(), 1, "1 combined entry found");
mAsserter.is(c.moveToFirst(), true, "Found combined entry with bookmark id");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(combinedReadingListItemId),
"Bookmark id should be set correctly on combined entry");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DISPLAY))), new Long(BrowserContract.Combined.DISPLAY_READER),
"Combined entry should have reader display type");
int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
BrowserContract.Bookmarks._ID + " = ?",
new String[] { String.valueOf(combinedReadingListItemId) });
mAsserter.is((deleted == 1), true, "Inserted combined reading list item was deleted");
c.close();
c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
mAsserter.is(c.getCount(), 1, "1 combined entry found");
mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
"Bookmark id should not be set to removed bookmark id");
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DISPLAY))), new Long(BrowserContract.Combined.DISPLAY_NORMAL),
"Combined entry should have reader display type");
c.close();
}
}
private class TestExpireHistory extends TestCase {
private void createFakeHistory(long timeShift, int count) {
// Insert a bunch of very new entries
@ -1778,8 +1716,8 @@ public class testBrowserProvider extends ContentProviderTest {
Log.w(LOGTAG, "after operation, notifyChangeList = " + mResolver.notifyChangeList);
}
mAsserter.is(Long.valueOf(mResolver.notifyChangeList.size()),
Long.valueOf(1),
mAsserter.is((long) mResolver.notifyChangeList.size(),
1L,
"Content observer was notified exactly once by " + operation);
Uri uri = mResolver.notifyChangeList.poll();
@ -1801,8 +1739,8 @@ public class testBrowserProvider extends ContentProviderTest {
mResolver.notifyChangeList.clear();
long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
mAsserter.isnot(Long.valueOf(id),
Long.valueOf(-1),
mAsserter.isnot(id,
-1L,
"Inserted item has valid id");
ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "insert");
@ -1815,8 +1753,8 @@ public class testBrowserProvider extends ContentProviderTest {
BrowserContract.History._ID + " = ?",
new String[] { String.valueOf(id) });
mAsserter.is(Long.valueOf(numUpdated),
Long.valueOf(1),
mAsserter.is(numUpdated,
1L,
"Correct number of items are updated");
ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "update");
@ -1825,8 +1763,8 @@ public class testBrowserProvider extends ContentProviderTest {
mResolver.notifyChangeList.clear();
long numDeleted = mProvider.delete(BrowserContract.History.CONTENT_URI, null, null);
mAsserter.is(Long.valueOf(numDeleted),
Long.valueOf(1),
mAsserter.is(numDeleted,
1L,
"Correct number of items are deleted");
ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "delete");
@ -1837,8 +1775,8 @@ public class testBrowserProvider extends ContentProviderTest {
mResolver.notifyChangeList.clear();
long numBulkInserted = mProvider.bulkInsert(BrowserContract.History.CONTENT_URI, hs);
mAsserter.is(Long.valueOf(numBulkInserted),
Long.valueOf(1),
mAsserter.is(numBulkInserted,
1L,
"Correct number of items are bulkInserted");
ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "bulkInsert");

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -916,14 +916,16 @@ SessionStore.prototype = {
let closedTabs = this._windows[aWindow.__SSID].closedTabs;
let tabs = closedTabs.map(function (tab) {
// Get the url and title for the last entry in the session history.
let lastEntry = tab.entries[tab.entries.length - 1];
return {
url: lastEntry.url,
title: lastEntry.title || ""
};
});
let tabs = closedTabs
.filter(tab => !tab.isPrivate)
.map(function (tab) {
// Get the url and title for the last entry in the session history.
let lastEntry = tab.entries[tab.entries.length - 1];
return {
url: lastEntry.url,
title: lastEntry.title || ""
};
});
sendMessageToJava({
type: "ClosedTabs:Data",

View File

@ -22,7 +22,6 @@ public class TestTopSitesCursorWrapper extends BrowserTestCase {
private String[] TOP_SITES_COLUMNS = new String[] { Combined._ID,
Combined.URL,
Combined.TITLE,
Combined.DISPLAY,
Combined.BOOKMARK_ID,
Combined.HISTORY_ID };
@ -49,7 +48,6 @@ public class TestTopSitesCursorWrapper extends BrowserTestCase {
row.add(-1);
row.add(TOP_PREFIX + "url" + i);
row.add(TOP_PREFIX + "title" + i);
row.add(Combined.DISPLAY_NORMAL);
row.add(i);
row.add(i);
}

View File

@ -14,5 +14,3 @@ EXTRA_COMPONENTS += \
httpd.manifest \
$(NULL)
endif
TESTING_JS_MODULES = httpd.js

View File

@ -16,4 +16,8 @@ EXTRA_COMPONENTS += [
'httpd.js',
]
JAR_MANIFESTS += ['jar.mn']
TESTING_JS_MODULES += [
'httpd.js',
]
JAR_MANIFESTS += ['jar.mn']

View File

@ -36,6 +36,7 @@ from ..frontend.data import (
IPDLFile,
JARManifest,
JavaJarData,
JavaScriptModules,
LibraryDefinition,
LocalInclude,
PerSourceFlag,
@ -450,6 +451,9 @@ class RecursiveMakeBackend(CommonBackend):
elif isinstance(obj, InstallationTarget):
self._process_installation_target(obj, backend_file)
elif isinstance(obj, JavaScriptModules):
self._process_javascript_modules(obj, backend_file)
elif isinstance(obj, SandboxWrapped):
# Process a rich build system object from the front-end
# as-is. Please follow precedent and handle CamelCaseData
@ -969,6 +973,20 @@ class RecursiveMakeBackend(CommonBackend):
if not obj.enabled:
backend_file.write('NO_DIST_INSTALL := 1\n')
def _process_javascript_modules(self, obj, backend_file):
if obj.flavor != 'testing':
raise Exception('We only support testing JavaScriptModules instances.')
if not self.environment.substs.get('ENABLE_TESTS', False):
return
manifest = self._install_manifests['tests']
def onmodule(source, dest, flags):
manifest.add_symlink(source, mozpath.join('modules', dest))
self._process_hierarchy(obj, obj.modules, '', onmodule)
def _handle_idl_manager(self, manager):
build_files = self._install_manifests['xpidl']

View File

@ -506,6 +506,21 @@ class JARManifest(SandboxDerived):
self.path = path
class JavaScriptModules(SandboxDerived):
"""Describes a JavaScript module."""
__slots__ = (
'modules',
'flavor',
)
def __init__(self, sandbox, modules, flavor):
super(JavaScriptModules, self).__init__(sandbox)
self.modules = modules
self.flavor = flavor
class SandboxWrapped(SandboxDerived):
"""Generic sandbox container object for a wrapped rich object.

View File

@ -33,6 +33,7 @@ from .data import (
InstallationTarget,
IPDLFile,
JARManifest,
JavaScriptModules,
LibraryDefinition,
LocalInclude,
PerSourceFlag,
@ -349,6 +350,10 @@ class TreeMetadataEmitter(LoggingMixin):
for program in sandbox['HOST_SIMPLE_PROGRAMS']:
yield HostSimpleProgram(sandbox, program, sandbox['CONFIG']['HOST_BIN_SUFFIX'])
test_js_modules = sandbox.get('TESTING_JS_MODULES')
if test_js_modules:
yield JavaScriptModules(sandbox, test_js_modules, 'testing')
simple_lists = [
('GENERATED_EVENTS_WEBIDL_FILES', GeneratedEventWebIDLFile),
('GENERATED_WEBIDL_FILES', GeneratedWebIDLFile),

View File

@ -210,6 +210,18 @@ VARIABLES = {
``JS_MODULES_PATH`` defaults to ``modules`` if left undefined.
""", 'libs'),
'TESTING_JS_MODULES': (HierarchicalStringList, list,
"""JavaScript modules to install in the test-only destination.
Some JavaScript modules (JSMs) are test-only and not distributed
with Firefox. This variable defines them.
To install modules in a subdirectory, use properties of this
variable to control the final destination. e.g.
``TESTING_JS_MODULES.foo += ['module.jsm']``.
""", 'libs'),
'EXTRA_PP_COMPONENTS': (StrictOrderingOnAppendList, list,
"""Javascript XPCOM files.

View File

@ -18,13 +18,6 @@ pp_modules := \
rest.js \
$(NULL)
testing_modules := \
bagheeraserver.js \
logging.js \
storageserver.js \
utils.js \
$(NULL)
JS_EXPORTS_FILES := $(srcdir)/services-common.js
JS_EXPORTS_DEST = $(FINAL_TARGET)/$(PREF_DIR)
INSTALL_TARGETS += JS_EXPORTS
@ -33,9 +26,6 @@ MODULES_FILES := $(modules)
MODULES_DEST = $(FINAL_TARGET)/modules/services-common
INSTALL_TARGETS += MODULES
TESTING_JS_MODULES := $(addprefix modules-testing/,$(testing_modules))
TESTING_JS_MODULE_DIR := services-common
PP_JS_MODULES := $(pp_modules)
PP_JS_MODULES_PATH = $(FINAL_TARGET)/modules/services-common
PP_TARGETS += PP_JS_MODULES

View File

@ -9,3 +9,10 @@ TEST_DIRS += ['tests']
EXTRA_COMPONENTS += [
'servicesComponents.manifest',
]
TESTING_JS_MODULES.services.common += [
'modules-testing/bagheeraserver.js',
'modules-testing/logging.js',
'modules-testing/storageserver.js',
'modules-testing/utils.js',
]

View File

@ -12,7 +12,7 @@
* $ make bagheera-server
*/
Cu.import("resource://testing-common/services-common/bagheeraserver.js");
Cu.import("resource://testing-common/services/common/bagheeraserver.js");
initTestLogging();

View File

@ -12,7 +12,7 @@
* $ make storage-server
*/
Cu.import("resource://testing-common/services-common/storageserver.js");
Cu.import("resource://testing-common/services/common/storageserver.js");
initTestLogging();

View File

@ -5,7 +5,7 @@
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://testing-common/services-common/logging.js");
Cu.import("resource://testing-common/services/common/logging.js");
let btoa = Cu.import("resource://gre/modules/Log.jsm").btoa;
let atob = Cu.import("resource://gre/modules/Log.jsm").atob;

View File

@ -5,7 +5,7 @@
Cu.import("resource://services-common/bagheeraclient.js");
Cu.import("resource://services-common/rest.js");
Cu.import("resource://testing-common/services-common/bagheeraserver.js");
Cu.import("resource://testing-common/services/common/bagheeraserver.js");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Task.jsm");

View File

@ -3,7 +3,7 @@
"use strict";
Cu.import("resource://testing-common/services-common/bagheeraserver.js");
Cu.import("resource://testing-common/services/common/bagheeraserver.js");
function run_test() {
run_next_test();

View File

@ -24,7 +24,7 @@ function run_test() {
}
for each (let m in test_modules) {
let resource = "resource://testing-common/services-common/" + m;
let resource = "resource://testing-common/services/common/" + m;
Components.utils.import(resource, {});
}
}

View File

@ -4,7 +4,7 @@
Cu.import("resource://services-common/async.js");
Cu.import("resource://services-common/rest.js");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://testing-common/services-common/storageserver.js");
Cu.import("resource://testing-common/services/common/storageserver.js");
const DEFAULT_USER = "123";
const DEFAULT_PASSWORD = "password";

View File

@ -2,7 +2,7 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-common/storageservice.js");
Cu.import("resource://testing-common/services-common/storageserver.js");
Cu.import("resource://testing-common/services/common/storageserver.js");
function run_test() {
initTestLogging("Trace");

View File

@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://testing-common/services-common/utils.js");
Cu.import("resource://testing-common/services/common/utils.js");
function run_test() {
let thing = {o: {foo: "foo", bar: ["bar"]}, a: ["foo", {bar: "bar"}]};

View File

@ -6,9 +6,6 @@ MODULES := policy.jsm sessions.jsm
MODULES_PATH = $(FINAL_TARGET)/modules/services/datareporting
PP_TARGETS += MODULES
TESTING_JS_MODULES := $(addprefix modules-testing/,mocks.jsm)
TESTING_JS_MODULE_DIR := services/datareporting
include $(topsrcdir)/config/rules.mk
$(FINAL_TARGET)/components/DataReportingService.js: policy.jsm sessions.jsm ../common/observers.js

View File

@ -13,3 +13,7 @@ EXTRA_COMPONENTS += [
EXTRA_PP_COMPONENTS += [
'DataReportingService.js',
]
TESTING_JS_MODULES.services.datareporting += [
'modules-testing/mocks.jsm',
]

View File

@ -8,7 +8,7 @@ do_get_profile();
(function initTestingInfrastructure() {
let ns = {};
Components.utils.import("resource://testing-common/services-common/logging.js",
Components.utils.import("resource://testing-common/services/common/logging.js",
ns);
ns.initTestLogging();

View File

@ -11,7 +11,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
do_get_profile();
let ns = {};
Cu.import("resource://testing-common/services-common/logging.js", ns);
Cu.import("resource://testing-common/services/common/logging.js", ns);
ns.initTestLogging("Trace");
}).call(this);

View File

@ -8,10 +8,6 @@ modules := \
providers.jsm \
$(NULL)
testing_modules := \
utils.jsm \
$(NULL)
healthreport_depends = \
HealthReport.jsm \
../common/async.js \
@ -36,9 +32,6 @@ MODULES_PATH = $(FINAL_TARGET)/modules/services/healthreport
MODULES_FLAGS := $(extra_pp_flags)
PP_TARGETS += MODULES
TESTING_JS_MODULES := $(addprefix modules-testing/,$(testing_modules))
TESTING_JS_MODULE_DIR := services/healthreport
include $(topsrcdir)/config/rules.mk
# Add extra prerequisites until bug 837792 is addressed.

View File

@ -11,3 +11,7 @@ TEST_DIRS += ['tests']
EXTRA_PP_COMPONENTS += [
'HealthReportComponents.manifest',
]
TESTING_JS_MODULES.services.healthreport += [
'modules-testing/utils.jsm',
]

View File

@ -8,7 +8,7 @@ do_get_profile();
(function initMetricsTestingInfrastructure() {
let ns = {};
Components.utils.import("resource://testing-common/services-common/logging.js",
Components.utils.import("resource://testing-common/services/common/logging.js",
ns);
ns.initTestLogging();

Some files were not shown because too many files have changed in this diff Show More