Merge m-c to mozilla-inbound.

This commit is contained in:
Ryan VanderMeulen 2013-08-30 13:43:55 -04:00
commit 90e148799a
34 changed files with 485 additions and 522 deletions

View File

@ -6,17 +6,10 @@
module.metadata = {
'stability': 'experimental',
'engines': {
'Firefox': '> 24'
'Firefox': '*'
}
};
try {
require('chrome').Cu.import('resource:///modules/CustomizableUI.jsm', {});
}
catch (e) {
throw Error('Unsupported Application: The module ' + module.id + ' does not support this application.');
}
const { Class } = require('../core/heritage');
const { merge } = require('../util/object');
const { Disposable } = require('../core/disposable');
@ -32,11 +25,9 @@ const { remove: removeFromArray } = require('../util/array');
const { show, hide, toggle } = require('./sidebar/actions');
const { Worker: WorkerTrait } = require('../content/worker');
const { contract: sidebarContract } = require('./sidebar/contract');
const { Button } = require('./button');
const { setStateFor, getStateFor } = require('./state');
const { create, dispose, updateTitle, updateURL, isSidebarShowing, showSidebar, hideSidebar } = require('./sidebar/view');
const { defer } = require('../core/promise');
const { models, buttons, views, viewsFor, modelFor } = require('./sidebar/namespace');
const { models, views, viewsFor, modelFor } = require('./sidebar/namespace');
const { isLocalURL } = require('../url');
const { ensure } = require('../system/unload');
@ -61,19 +52,6 @@ const Sidebar = Class({
validateTitleAndURLCombo({}, this.title, this.url);
// NOTE: delegating icon validation to the Button.
// IMPORTANT: Make the button first since it has it's own
// validation which we make use of.. (even if the sidebar
// id is not a duplicate the button id could be..)
let button = Button({
id: model.id,
icon: model.icon,
label: model.title,
type: 'checkbox',
onChange: update.bind(null, 'button')
});
buttons.set(this, button);
const self = this;
const internals = sidebarNS(self);
const windowNS = internals.windowNS = ns();
@ -83,28 +61,6 @@ const Sidebar = Class({
setListeners(this, options);
function update(source, state) {
let wins = windows('navigator:browser', { includePrivate: true });
for (let window of wins) {
let isShowing = isSidebarShowing(window, self);
let isChecked = (source == 'button') ? getStateFor(button, window).checked : isShowing;
// update sidebar?
if (isShowing != isChecked) {
if (isChecked) {
showSidebar(window, self);
}
else {
hideSidebar(window, self);
}
}
// update the button
setStateFor(button, window, { checked: isChecked });
}
}
let bars = [];
internals.tracker = WindowTracker({
onTrack: function(window) {
@ -160,7 +116,6 @@ const Sidebar = Class({
// uncheck the associated menuitem
bar.setAttribute('checked', 'false');
setStateFor(button, window, { checked: false });
emit(self, 'hide', {});
emit(self, 'detach', worker);
@ -175,8 +130,6 @@ const Sidebar = Class({
panelBrowser.contentWindow.removeEventListener('load', onWebPanelSidebarLoad, true);
windowNS(window).onWebPanelSidebarLoad = null;
update();
// TODO: decide if returning worker is acceptable..
//emit(self, 'show', { worker: worker });
emit(self, 'show', {});
@ -263,13 +216,6 @@ const Sidebar = Class({
updateURL(this, v);
modelFor(this).url = v;
},
get icon() (buttons.get(this) || {}).icon,
set icon(v) {
let button = buttons.get(this);
if (!button)
return;
button.icon = v;
},
show: function() {
return showSidebar(null, this);
},
@ -291,11 +237,6 @@ const Sidebar = Class({
views.delete(this);
models.delete(this);
// kill the button
let button = buttons.get(this);
if (button)
button.destroy();
}
});
exports.Sidebar = Sidebar;

View File

@ -7,11 +7,6 @@ const { contract } = require('../../util/contract');
const { isValidURI, URL, isLocalURL } = require('../../url');
const { isNil, isObject, isString } = require('../../lang/type');
function isIconSet(icons) {
return Object.keys(icons).
every(size => String(size >>> 0) === size && isLocalURL(icons[size]))
}
exports.contract = contract({
id: {
is: [ 'string' ],
@ -23,12 +18,6 @@ exports.contract = contract({
is: [ 'string' ],
ok: v => v.length
},
icon: {
is: ['string', 'object'],
ok: v => (isString(v) && isLocalURL(v)) || (isObject(v) && isIconSet(v)),
msg: 'The option "icon" must be a local URL or an object with ' +
'numeric keys / local URL values pair.'
},
url: {
is: [ 'string' ],
ok: v => isLocalURL(v),

View File

@ -6,7 +6,7 @@
module.metadata = {
'stability': 'unstable',
'engines': {
'Firefox': '> 24'
'Firefox': '*'
}
};
@ -20,10 +20,14 @@ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const WEB_PANEL_BROWSER_ID = 'web-panels-browser';
function create(window, details) {
let id = makeID(details.id);
let { document } = window;
if (document.getElementById(id))
throw new Error('The ID "' + details.id + '" seems already used.');
let menuitem = document.createElementNS(XUL_NS, 'menuitem');
menuitem.setAttribute('id', makeID(details.id));
menuitem.setAttribute('id', id);
menuitem.setAttribute('label', details.title);
menuitem.setAttribute('sidebarurl', details.sidebarurl);
menuitem.setAttribute('checked', 'false');

View File

@ -7,8 +7,6 @@ const { Cu } = require('chrome');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { fromIterator } = require('sdk/util/array');
const BLANK_IMG = exports.BLANK_IMG = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
const BUILTIN_SIDEBAR_MENUITEMS = exports.BUILTIN_SIDEBAR_MENUITEMS = [
'menu_socialSidebar',
'menu_historySidebar',
@ -61,23 +59,6 @@ function simulateClick(ele) {
}
exports.simulateClick = simulateClick;
function getWidget(buttonId, window = getMostRecentBrowserWindow()) {
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
const { AREA_NAVBAR } = CustomizableUI;
let widgets = CustomizableUI.getWidgetsInArea(AREA_NAVBAR).
filter(({id}) => id.startsWith('button--') && id.endsWith(buttonId));
if (widgets.length === 0)
throw new Error('Widget with id `' + buttonId +'` not found.');
if (widgets.length > 1)
throw new Error('Unexpected number of widgets: ' + widgets.length)
return widgets[0].forWindow(window);
};
exports.getWidget = getWidget;
// OSX and Windows exhibit different behaviors when 'checked' is false,
// so compare against the consistent 'true'. See bug 894809.
function isChecked(node) {

View File

@ -13,9 +13,9 @@ const { isPrivate } = require('sdk/private-browsing');
const { data } = require('sdk/self');
const { URL } = require('sdk/url');
const { BLANK_IMG, BUILTIN_SIDEBAR_MENUITEMS, isSidebarShowing,
const { BUILTIN_SIDEBAR_MENUITEMS, isSidebarShowing,
getSidebarMenuitems, getExtraSidebarMenuitems, makeID, simulateCommand,
simulateClick, getWidget, isChecked } = require('./sidebar/utils');
simulateClick, isChecked } = require('./sidebar/utils');
exports.testSideBarIsInNewPrivateWindows = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
@ -23,7 +23,6 @@ exports.testSideBarIsInNewPrivateWindows = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
@ -44,6 +43,8 @@ exports.testSideBarIsInNewPrivateWindows = function(assert, done) {
})
}
// Disabled in order to land other fixes, see bug 910647 for further details.
/*
exports.testSidebarIsOpenInNewPrivateWindow = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testSidebarIsOpenInNewPrivateWindow';
@ -52,7 +53,6 @@ exports.testSidebarIsOpenInNewPrivateWindow = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
@ -87,7 +87,7 @@ exports.testSidebarIsOpenInNewPrivateWindow = function(assert, done) {
sidebar.show();
}
*/
// TEST: edge case where web panel is destroyed while loading
exports.testDestroyEdgeCaseBugWithPrivateWindow = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
@ -96,7 +96,6 @@ exports.testDestroyEdgeCaseBugWithPrivateWindow = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
@ -125,14 +124,13 @@ exports.testDestroyEdgeCaseBugWithPrivateWindow = function(assert, done) {
let sidebar = loader.require('sdk/ui/sidebar').Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+ testName,
onShow: function() {
assert.pass('onShow works for Sidebar');
loader.unload();
let sidebarMI = getSidebarMenuitems();
for each (let mi in sidebarMI) {
for (let mi of sidebarMI) {
assert.ok(BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) >= 0, 'the menuitem is for a built-in sidebar')
assert.ok(!isChecked(mi), 'no sidebar menuitem is checked');
}
@ -159,7 +157,6 @@ exports.testShowInPrivateWindow = function(assert, done) {
let sidebar1 = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
let menuitemID = makeID(sidebar1.id);

View File

@ -5,7 +5,7 @@
module.metadata = {
'engines': {
'Firefox': '> 24'
'Firefox': '*'
}
};
@ -13,8 +13,6 @@ const { Cu } = require('chrome');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { fromIterator } = require('sdk/util/array');
const BLANK_IMG = exports.BLANK_IMG = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
const BUILTIN_SIDEBAR_MENUITEMS = exports.BUILTIN_SIDEBAR_MENUITEMS = [
'menu_socialSidebar',
'menu_historySidebar',
@ -67,23 +65,6 @@ function simulateClick(ele) {
}
exports.simulateClick = simulateClick;
function getWidget(buttonId, window = getMostRecentBrowserWindow()) {
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
const { AREA_NAVBAR } = CustomizableUI;
let widgets = CustomizableUI.getWidgetsInArea(AREA_NAVBAR).
filter(({id}) => id.startsWith('button--') && id.endsWith(buttonId));
if (widgets.length === 0)
throw new Error('Widget with id `' + buttonId +'` not found.');
if (widgets.length > 1)
throw new Error('Unexpected number of widgets: ' + widgets.length)
return widgets[0].forWindow(window);
};
exports.getWidget = getWidget;
// OSX and Windows exhibit different behaviors when 'checked' is false,
// so compare against the consistent 'true'. See bug 894809.
function isChecked(node) {

View File

@ -5,7 +5,7 @@
module.metadata = {
'engines': {
'Firefox': '> 24'
'Firefox': '*'
}
};
@ -19,9 +19,9 @@ const { isPrivate } = require('sdk/private-browsing');
const { data } = require('sdk/self');
const { URL } = require('sdk/url');
const { BLANK_IMG, BUILTIN_SIDEBAR_MENUITEMS, isSidebarShowing,
const { BUILTIN_SIDEBAR_MENUITEMS, isSidebarShowing,
getSidebarMenuitems, getExtraSidebarMenuitems, makeID, simulateCommand,
simulateClick, getWidget, isChecked } = require('./sidebar/utils');
simulateClick, isChecked } = require('./sidebar/utils');
exports.testSideBarIsNotInNewPrivateWindows = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
@ -29,7 +29,6 @@ exports.testSideBarIsNotInNewPrivateWindows = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
@ -58,7 +57,6 @@ exports.testSidebarIsNotOpenInNewPrivateWindow = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
@ -94,7 +92,6 @@ exports.testDestroyEdgeCaseBugWithPrivateWindow = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
@ -123,14 +120,13 @@ exports.testDestroyEdgeCaseBugWithPrivateWindow = function(assert, done) {
let sidebar = loader.require('sdk/ui/sidebar').Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+ testName,
onShow: function() {
assert.pass('onShow works for Sidebar');
loader.unload();
let sidebarMI = getSidebarMenuitems();
for each (let mi in sidebarMI) {
for (let mi of sidebarMI) {
assert.ok(BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) >= 0, 'the menuitem is for a built-in sidebar')
assert.ok(!isChecked(mi), 'no sidebar menuitem is checked');
}
@ -158,7 +154,6 @@ exports.testShowInPrivateWindow = function(assert, done) {
let sidebar1 = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});

View File

@ -5,7 +5,7 @@
module.metadata = {
'engines': {
'Firefox': '> 24'
'Firefox': '*'
}
};
@ -22,9 +22,9 @@ const { URL } = require('sdk/url');
const { once, off, emit } = require('sdk/event/core');
const { defer, all } = require('sdk/core/promise');
const { BLANK_IMG, BUILTIN_SIDEBAR_MENUITEMS, isSidebarShowing,
const { BUILTIN_SIDEBAR_MENUITEMS, isSidebarShowing,
getSidebarMenuitems, getExtraSidebarMenuitems, makeID, simulateCommand,
simulateClick, getWidget, isChecked } = require('./sidebar/utils');
simulateClick, isChecked } = require('./sidebar/utils');
exports.testSidebarBasicLifeCycle = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
@ -39,15 +39,12 @@ exports.testSidebarBasicLifeCycle = function(assert, done) {
let sidebarDetails = {
id: testName,
title: 'test',
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
};
let sidebar = Sidebar(sidebarDetails);
// test the sidebar attributes
for each(let key in Object.keys(sidebarDetails)) {
if (key == 'icon')
continue;
for (let key of Object.keys(sidebarDetails)) {
assert.equal(sidebarDetails[key], sidebar[key], 'the attributes match the input');
}
@ -81,7 +78,7 @@ exports.testSidebarBasicLifeCycle = function(assert, done) {
sidebar.destroy();
let sidebarMI = getSidebarMenuitems();
for each (let mi in sidebarMI) {
for (let mi of sidebarMI) {
assert.ok(BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) >= 0, 'the menuitem is for a built-in sidebar')
assert.ok(!isChecked(mi), 'no sidebar menuitem is checked');
}
@ -107,7 +104,6 @@ exports.testSideBarIsInNewWindows = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
@ -136,7 +132,6 @@ exports.testSideBarIsShowingInNewWindows = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: URL('data:text/html;charset=utf-8,'+testName)
});
@ -206,7 +201,6 @@ exports.testAddonGlobalSimple = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: data.url('test-sidebar-addon-global.html')
});
@ -233,7 +227,6 @@ exports.testAddonGlobalComplex = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: data.url('test-sidebar-addon-global.html')
});
@ -265,13 +258,11 @@ exports.testShowingOneSidebarAfterAnother = function(assert, done) {
let sidebar1 = Sidebar({
id: testName + '1',
title: testName + '1',
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+ testName + 1
});
let sidebar2 = Sidebar({
id: testName + '2',
title: testName + '2',
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+ testName + 2
});
@ -290,7 +281,7 @@ exports.testShowingOneSidebarAfterAnother = function(assert, done) {
sidebar1.once('show', function() {
testShowing(true, false, true);
for each (let mi in getExtraSidebarMenuitems(window)) {
for (let mi of getExtraSidebarMenuitems(window)) {
let menuitemID = mi.getAttribute('id').replace(/^jetpack-sidebar-/, '');
assert.ok(IDs.indexOf(menuitemID) >= 0, 'the extra menuitem is for one of our test sidebars');
assert.equal(isChecked(mi), menuitemID == sidebar1.id, 'the test sidebar menuitem has the correct checked value');
@ -298,7 +289,7 @@ exports.testShowingOneSidebarAfterAnother = function(assert, done) {
sidebar2.once('show', function() {
testShowing(false, true, true);
for each (let mi in getExtraSidebarMenuitems(window)) {
for (let mi of getExtraSidebarMenuitems(window)) {
let menuitemID = mi.getAttribute('id').replace(/^jetpack-sidebar-/, '');
assert.ok(IDs.indexOf(menuitemID) >= 0, 'the extra menuitem is for one of our test sidebars');
assert.equal(isChecked(mi), menuitemID == sidebar2.id, 'the test sidebar menuitem has the correct checked value');
@ -328,18 +319,16 @@ exports.testSidebarUnload = function(assert, done) {
assert.equal(isPrivate(window), false, 'the current window is not private');
// EXPLICIT: testing require('sdk/ui')
let sidebar = loader.require('sdk/ui').Sidebar({
let sidebar = loader.require('sdk/ui/sidebar').Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+ testName,
onShow: function() {
assert.pass('onShow works for Sidebar');
loader.unload();
let sidebarMI = getSidebarMenuitems();
for each (let mi in sidebarMI) {
for (let mi of sidebarMI) {
assert.ok(BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) >= 0, 'the menuitem is for a built-in sidebar')
assert.ok(!isChecked(mi), 'no sidebar menuitem is checked');
}
@ -361,7 +350,6 @@ exports.testRemoteContent = function(assert) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'http://dne.xyz.mozilla.org'
});
assert.fail('a bad sidebar was created..');
@ -379,7 +367,6 @@ exports.testInvalidURL = function(assert) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'http:mozilla.org'
});
assert.fail('a bad sidebar was created..');
@ -396,8 +383,7 @@ exports.testInvalidURLType = function(assert) {
try {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG
title: testName
});
assert.fail('a bad sidebar was created..');
sidebar.destroy();
@ -414,7 +400,6 @@ exports.testInvalidTitle = function(assert) {
let sidebar = Sidebar({
id: testName,
title: '',
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
assert.fail('a bad sidebar was created..');
@ -425,23 +410,6 @@ exports.testInvalidTitle = function(assert) {
}
}
exports.testInvalidIcon = function(assert) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testInvalidIcon';
try {
let sidebar = Sidebar({
id: testName,
title: testName,
url: 'data:text/html;charset=utf-8,'+testName
});
assert.fail('a bad sidebar was created..');
sidebar.destroy();
}
catch(e) {
assert.ok(/The option "icon" must be a local URL or an object with/.test(e), 'invalid icons are not acceptable');
}
}
exports.testInvalidID = function(assert) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testInvalidID';
@ -449,7 +417,6 @@ exports.testInvalidID = function(assert) {
let sidebar = Sidebar({
id: '!',
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
assert.fail('a bad sidebar was created..');
@ -467,7 +434,6 @@ exports.testInvalidBlankID = function(assert) {
let sidebar = Sidebar({
id: '',
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
assert.fail('a bad sidebar was created..');
@ -485,7 +451,6 @@ exports.testInvalidNullID = function(assert) {
let sidebar = Sidebar({
id: null,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
assert.fail('a bad sidebar was created..');
@ -502,7 +467,6 @@ exports.testInvalidUndefinedID = function(assert) {
try {
let sidebar = Sidebar({
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
assert.fail('a bad sidebar was created..');
@ -521,7 +485,6 @@ exports.testDestroyEdgeCaseBug = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
@ -550,14 +513,13 @@ exports.testDestroyEdgeCaseBug = function(assert, done) {
let sidebar = loader.require('sdk/ui/sidebar').Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+ testName,
onShow: function() {
assert.pass('onShow works for Sidebar');
loader.unload();
let sidebarMI = getSidebarMenuitems();
for each (let mi in sidebarMI) {
for (let mi of sidebarMI) {
assert.ok(BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) >= 0, 'the menuitem is for a built-in sidebar')
assert.ok(!isChecked(mi), 'no sidebar menuitem is checked');
}
@ -582,7 +544,6 @@ exports.testClickingACheckedMenuitem = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName,
});
@ -600,61 +561,6 @@ exports.testClickingACheckedMenuitem = function(assert, done) {
});
};
exports.testClickingACheckedButton = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testClickingACheckedButton';
let window = getMostRecentBrowserWindow();
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName,
onShow: function onShow() {
sidebar.off('show', onShow);
assert.pass('the sidebar was shown');
//assert.equal(button.checked, true, 'the button is now checked');
sidebar.once('hide', function() {
assert.pass('clicking the button after the sidebar has shown hides it.');
sidebar.once('show', function() {
assert.pass('clicking the button again shows it.');
sidebar.hide().then(function() {
assert.pass('hide callback works');
assert.equal(isShowing(sidebar), false, 'the sidebar is not showing, final.');
assert.pass('the sidebar was destroying');
sidebar.destroy();
assert.pass('the sidebar was destroyed');
assert.equal(button.parentNode, null, 'the button\'s parents were shot')
done();
}, assert.fail);
});
assert.equal(isShowing(sidebar), false, 'the sidebar is not showing');
// TODO: figure out why this is necessary..
setTimeout(function() simulateCommand(button));
});
assert.equal(isShowing(sidebar), true, 'the sidebar is showing');
simulateCommand(button);
}
});
let { node: button } = getWidget(sidebar.id, window);
//assert.equal(button.checked, false, 'the button exists and is not checked');
assert.equal(isShowing(sidebar), false, 'the sidebar is not showing');
simulateCommand(button);
}
exports.testTitleSetter = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testTitleSetter';
@ -663,16 +569,12 @@ exports.testTitleSetter = function(assert, done) {
let sidebar1 = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName,
});
assert.equal(sidebar1.title, testName, 'title getter works');
sidebar1.show().then(function() {
let button = document.querySelector('toolbarbutton[label=' + testName + ']');
assert.ok(button, 'button was found');
assert.equal(document.getElementById(makeID(sidebar1.id)).getAttribute('label'),
testName,
'the menuitem label is correct');
@ -689,8 +591,6 @@ exports.testTitleSetter = function(assert, done) {
assert.equal(document.getElementById('sidebar-title').value, 'foo', 'the sidebar title was updated');
assert.equal(button.getAttribute('label'), 'foo', 'the button label was updated');
sidebar1.destroy();
done();
}, assert.fail);
@ -706,7 +606,6 @@ exports.testURLSetter = function(assert, done) {
let sidebar1 = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
@ -759,7 +658,6 @@ exports.testDuplicateID = function(assert) {
let sidebar1 = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
@ -767,7 +665,6 @@ exports.testDuplicateID = function(assert) {
Sidebar({
id: testName,
title: testName + 1,
icon: BLANK_IMG,
url: url + 2
}).destroy();
}, /The ID .+ seems already used\./i, 'duplicate IDs will throw errors');
@ -785,7 +682,6 @@ exports.testURLSetterToSameValueReloadsSidebar = function(assert, done) {
let sidebar1 = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
@ -828,16 +724,15 @@ exports.testURLSetterToSameValueReloadsSidebar = function(assert, done) {
}, assert.fail);
}
exports.testButtonShowingInOneWindowDoesNotAffectOtherWindows = function(assert, done) {
exports.testShowingInOneWindowDoesNotAffectOtherWindows = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testButtonShowingInOneWindowDoesNotAffectOtherWindows';
let testName = 'testShowingInOneWindowDoesNotAffectOtherWindows';
let window1 = getMostRecentBrowserWindow();
let url = 'data:text/html;charset=utf-8,'+testName;
let sidebar1 = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
@ -860,7 +755,7 @@ exports.testButtonShowingInOneWindowDoesNotAffectOtherWindows = function(assert,
let { document } = window;
assert.pass('new window was opened!');
// waiting for show using button
// waiting for show
sidebar1.once('show', function() {
// check state of the new window
assert.equal(isShowing(sidebar1), true, 'the sidebar is showing');
@ -899,11 +794,7 @@ exports.testButtonShowingInOneWindowDoesNotAffectOtherWindows = function(assert,
assert.pass('set sidebar1.url');
});
// clicking the sidebar button on the second window
let { node: button } = getWidget(sidebar1.id, window);
assert.ok(!!button, 'the button was found!');
simulateCommand(button);
sidebar1.show();
}, assert.fail);
}
@ -914,7 +805,6 @@ exports.testHidingAHiddenSidebarRejects = function(assert) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
@ -938,7 +828,6 @@ exports.testGCdSidebarsOnUnload = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
@ -947,17 +836,14 @@ exports.testGCdSidebarsOnUnload = function(assert, done) {
assert.equal(isSidebarShowing(window), true, 'the sidebar is showing');
let buttonID = getWidget(testName, window).node.getAttribute('id');
let menuitemID = makeID(testName);
assert.ok(!!window.document.getElementById(buttonID), 'the button was found');
assert.ok(!!window.document.getElementById(menuitemID), 'the menuitem was found');
Cu.schedulePreciseGC(function() {
loader.unload();
assert.equal(isSidebarShowing(window), false, 'the sidebar is not showing after unload');
assert.ok(!window.document.getElementById(buttonID), 'the button was removed');
assert.ok(!window.document.getElementById(menuitemID), 'the menuitem was removed');
done();
@ -978,7 +864,6 @@ exports.testGCdShowingSidebarsOnUnload = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
@ -987,21 +872,17 @@ exports.testGCdShowingSidebarsOnUnload = function(assert, done) {
assert.equal(isSidebarShowing(window), true, 'the sidebar is showing');
let buttonID = getWidget(testName, window).node.getAttribute('id');
let menuitemID = makeID(testName);
assert.ok(!!window.document.getElementById(buttonID), 'the button was found');
assert.ok(!!window.document.getElementById(menuitemID), 'the menuitem was found');
Cu.schedulePreciseGC(function() {
assert.equal(isSidebarShowing(window), true, 'the sidebar is still showing after gc');
assert.ok(!!window.document.getElementById(buttonID), 'the button was found after gc');
assert.ok(!!window.document.getElementById(menuitemID), 'the menuitem was found after gc');
loader.unload();
assert.equal(isSidebarShowing(window), false, 'the sidebar is not showing after unload');
assert.ok(!window.document.getElementById(buttonID), 'the button was removed');
assert.ok(!window.document.getElementById(menuitemID), 'the menuitem was removed');
done();
@ -1025,23 +906,18 @@ exports.testGCdHiddenSidebarsOnUnload = function(assert, done) {
Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
let buttonID = getWidget(testName, window).node.getAttribute('id');
let menuitemID = makeID(testName);
assert.ok(!!window.document.getElementById(buttonID), 'the button was found');
assert.ok(!!window.document.getElementById(menuitemID), 'the menuitem was found');
Cu.schedulePreciseGC(function() {
assert.ok(!!window.document.getElementById(buttonID), 'the button was found after gc');
assert.ok(!!window.document.getElementById(menuitemID), 'the menuitem was found after gc');
loader.unload();
assert.ok(!window.document.getElementById(buttonID), 'the button was removed');
assert.ok(!window.document.getElementById(menuitemID), 'the menuitem was removed');
done();
@ -1056,7 +932,6 @@ exports.testSidebarGettersAndSettersAfterDestroy = function(assert) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
@ -1079,61 +954,6 @@ exports.testSidebarGettersAndSettersAfterDestroy = function(assert) {
assert.equal(sidebar.url, undefined, 'sidebar after destroy has no url');
}
exports.testButtonIconSet = function(assert) {
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
let loader = Loader(module);
let { Sidebar } = loader.require('sdk/ui');
let testName = 'testButtonIconSet';
let url = 'data:text/html;charset=utf-8,'+testName;
// Test remote icon set
assert.throws(
() => Sidebar({
id: 'my-button-10',
title: 'my button',
url: url,
icon: {
'16': 'http://www.mozilla.org/favicon.ico'
}
}),
/^The option "icon"/,
'throws on no valid icon given');
let sidebar = Sidebar({
id: 'my-button-11',
title: 'my button',
url: url,
icon: {
'16': './icon16.png',
'32': './icon32.png',
'64': './icon64.png'
}
});
let { node, id: widgetId } = getWidget(sidebar.id);
let { devicePixelRatio } = node.ownerDocument.defaultView;
let size = 16 * devicePixelRatio;
assert.equal(node.getAttribute('image'), data.url(sidebar.icon[size].substr(2)),
'the icon is set properly in navbar');
let size = 32 * devicePixelRatio;
CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL);
assert.equal(node.getAttribute('image'), data.url(sidebar.icon[size].substr(2)),
'the icon is set properly in panel');
// Using `loader.unload` without move back the button to the original area
// raises an error in the CustomizableUI. This is doesn't happen if the
// button is moved manually from navbar to panel. I believe it has to do
// with `addWidgetToArea` method, because even with a `timeout` the issue
// persist.
CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
loader.unload();
}
exports.testSidebarLeakCheckDestroyAfterAttach = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
@ -1142,7 +962,6 @@ exports.testSidebarLeakCheckDestroyAfterAttach = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
@ -1184,7 +1003,6 @@ exports.testSidebarLeakCheckUnloadAfterAttach = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,'+testName
});
@ -1228,7 +1046,6 @@ exports.testTwoSidebarsWithSameTitleAndURL = function(assert) {
let sidebar1 = Sidebar({
id: testName + 1,
title: title,
icon: BLANK_IMG,
url: url
});
@ -1236,7 +1053,6 @@ exports.testTwoSidebarsWithSameTitleAndURL = function(assert) {
Sidebar({
id: testName + 2,
title: title,
icon: BLANK_IMG,
url: url
}).destroy();
}, /title.+url.+invalid/i, 'Creating two sidebars with the same title + url is not allowed');
@ -1244,7 +1060,6 @@ exports.testTwoSidebarsWithSameTitleAndURL = function(assert) {
let sidebar2 = Sidebar({
id: testName + 2,
title: title,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,X'
});
@ -1263,9 +1078,9 @@ exports.testTwoSidebarsWithSameTitleAndURL = function(assert) {
sidebar2.destroy();
}
exports.testButtonToOpenXToClose = function(assert, done) {
exports.testShowToOpenXToClose = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testButtonToOpenXToClose';
let testName = 'testShowToOpenXToClose';
let title = testName;
let url = 'data:text/html;charset=utf-8,' + testName;
@ -1274,17 +1089,14 @@ exports.testButtonToOpenXToClose = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url,
onShow: function() {
assert.ok(isChecked(button), 'button is checked');
assert.ok(isChecked(menuitem), 'menuitem is checked');
let closeButton = window.document.querySelector('#sidebar-header > toolbarbutton.tabs-closebutton');
simulateCommand(closeButton);
},
onHide: function() {
assert.ok(!isChecked(button), 'button is not checked');
assert.ok(!isChecked(menuitem), 'menuitem is not checked');
sidebar.destroy();
@ -1292,18 +1104,16 @@ exports.testButtonToOpenXToClose = function(assert, done) {
}
});
let { node: button } = getWidget(sidebar.id, window);
let menuitem = window.document.getElementById(makeID(sidebar.id));
assert.ok(!isChecked(button), 'button is not checked');
assert.ok(!isChecked(menuitem), 'menuitem is not checked');
simulateCommand(button);
sidebar.show();
}
exports.testButtonToOpenMenuitemToClose = function(assert, done) {
exports.testShowToOpenMenuitemToClose = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testButtonToOpenMenuitemToClose';
let testName = 'testShowToOpenMenuitemToClose';
let title = testName;
let url = 'data:text/html;charset=utf-8,' + testName;
@ -1312,16 +1122,13 @@ exports.testButtonToOpenMenuitemToClose = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url,
onShow: function() {
assert.ok(isChecked(button), 'button is checked');
assert.ok(isChecked(menuitem), 'menuitem is checked');
simulateCommand(menuitem);
},
onHide: function() {
assert.ok(!isChecked(button), 'button is not checked');
assert.ok(!isChecked(menuitem), 'menuitem is not checked');
sidebar.destroy();
@ -1329,13 +1136,11 @@ exports.testButtonToOpenMenuitemToClose = function(assert, done) {
}
});
let { node: button } = getWidget(sidebar.id, window);
let menuitem = window.document.getElementById(makeID(sidebar.id));
assert.ok(!isChecked(button), 'button is not checked');
assert.ok(!isChecked(menuitem), 'menuitem is not checked');
simulateCommand(button);
sidebar.show();
}
exports.testDestroyWhileNonBrowserWindowIsOpen = function(assert, done) {
@ -1346,7 +1151,6 @@ exports.testDestroyWhileNonBrowserWindowIsOpen = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: url
});
@ -1393,7 +1197,6 @@ exports.testEventListeners = function(assert, done) {
let sidebar = Sidebar({
id: testName,
title: testName,
icon: BLANK_IMG,
url: 'data:text/html;charset=utf-8,' + testName,
onShow: function() {
assert.equal(this, sidebar, '`this` is correct in onShow');

View File

@ -197,6 +197,8 @@ let FormAssistant = {
addMessageListener("Forms:GetText", this);
addMessageListener("Forms:Input:SendKey", this);
addMessageListener("Forms:GetContext", this);
addMessageListener("Forms:SetComposition", this);
addMessageListener("Forms:EndComposition", this);
},
ignoredInputTypes: new Set([
@ -239,6 +241,7 @@ let FormAssistant = {
if (this.focusedElement) {
this.focusedElement.removeEventListener('mousedown', this);
this.focusedElement.removeEventListener('mouseup', this);
this.focusedElement.removeEventListener('compositionend', this);
if (this._observer) {
this._observer.disconnect();
this._observer = null;
@ -263,6 +266,7 @@ let FormAssistant = {
if (element) {
element.addEventListener('mousedown', this);
element.addEventListener('mouseup', this);
element.addEventListener('compositionend', this);
if (isContentEditable(element)) {
this._documentEncoder = getDocumentEncoder(element);
}
@ -423,6 +427,8 @@ let FormAssistant = {
break;
}
CompositionManager.endComposition('');
// Don't monitor the text change resulting from key event.
this._ignoreEditActionOnce = true;
@ -438,8 +444,18 @@ let FormAssistant = {
break;
}
CompositionManager.endComposition('');
this._ignoreEditActionOnce = false;
break;
case "compositionend":
if (!this.focusedElement) {
break;
}
CompositionManager.onCompositionEnd();
break;
}
},
@ -475,6 +491,8 @@ let FormAssistant = {
this._editing = true;
switch (msg.name) {
case "Forms:Input:Value": {
CompositionManager.endComposition('');
target.value = json.value;
let event = target.ownerDocument.createEvent('HTMLEvents');
@ -484,6 +502,8 @@ let FormAssistant = {
}
case "Forms:Input:SendKey":
CompositionManager.endComposition('');
["keydown", "keypress", "keyup"].forEach(function(type) {
domWindowUtils.sendKeyEvent(type, json.keyCode, json.charCode,
json.modifiers);
@ -528,6 +548,8 @@ let FormAssistant = {
}
case "Forms:SetSelectionRange": {
CompositionManager.endComposition('');
let start = json.selectionStart;
let end = json.selectionEnd;
setSelectionRange(target, start, end);
@ -543,6 +565,8 @@ let FormAssistant = {
}
case "Forms:ReplaceSurroundingText": {
CompositionManager.endComposition('');
let text = json.text;
let beforeLength = json.beforeLength;
let afterLength = json.afterLength;
@ -583,6 +607,23 @@ let FormAssistant = {
sendAsyncMessage("Forms:GetContext:Result:OK", obj);
break;
}
case "Forms:SetComposition": {
CompositionManager.setComposition(target, json.text, json.cursor,
json.clauses);
sendAsyncMessage("Forms:SetComposition:Result:OK", {
requestId: json.requestId,
});
break;
}
case "Forms:EndComposition": {
CompositionManager.endComposition(json.text);
sendAsyncMessage("Forms:EndComposition:Result:OK", {
requestId: json.requestId,
});
break;
}
}
this._editing = false;
@ -1015,3 +1056,97 @@ function replaceSurroundingText(element, text, selectionStart, beforeLength,
editor.insertText(text);
}
}
let CompositionManager = {
_isStarted: false,
_text: '',
_clauseAttrMap: {
'raw-input': domWindowUtils.COMPOSITION_ATTR_RAWINPUT,
'selected-raw-text': domWindowUtils.COMPOSITION_ATTR_SELECTEDRAWTEXT,
'converted-text': domWindowUtils.COMPOSITION_ATTR_CONVERTEDTEXT,
'selected-converted-text': domWindowUtils.COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT
},
setComposition: function cm_setComposition(element, text, cursor, clauses) {
// Check parameters.
if (!element) {
return;
}
let len = text.length;
if (cursor < 0) {
cursor = 0;
} else if (cursor > len) {
cursor = len;
}
let clauseLens = [len, 0, 0];
let clauseAttrs = [domWindowUtils.COMPOSITION_ATTR_RAWINPUT,
domWindowUtils.COMPOSITION_ATTR_RAWINPUT,
domWindowUtils.COMPOSITION_ATTR_RAWINPUT];
if (clauses) {
let remainingLength = len;
// Currently we don't support 4 or more clauses composition string.
let clauseNum = Math.min(3, clauses.length);
for (let i = 0; i < clauseNum; i++) {
if (clauses[i]) {
let clauseLength = clauses[i].length || 0;
// Make sure the total clauses length is not bigger than that of the
// composition string.
if (clauseLength > remainingLength) {
clauseLength = remainingLength;
}
remainingLength -= clauseLength;
clauseLens[i] = clauseLength;
clauseAttrs[i] = this._clauseAttrMap[clauses[i].selectionType] ||
domWindowUtils.COMPOSITION_ATTR_RAWINPUT;
}
}
// If the total clauses length is less than that of the composition
// string, extend the last clause to the end of the composition string.
if (remainingLength > 0) {
clauseLens[2] += remainingLength;
}
}
// Start composition if need to.
if (!this._isStarted) {
this._isStarted = true;
domWindowUtils.sendCompositionEvent('compositionstart', '', '');
this._text = '';
}
// Update the composing text.
if (this._text !== text) {
this._text = text;
domWindowUtils.sendCompositionEvent('compositionupdate', text, '');
}
domWindowUtils.sendTextEvent(text,
clauseLens[0], clauseAttrs[0],
clauseLens[1], clauseAttrs[1],
clauseLens[2], clauseAttrs[2],
cursor, 0);
},
endComposition: function cm_endComposition(text) {
if (!this._isStarted) {
return;
}
// Update the composing text.
if (this._text !== text) {
domWindowUtils.sendCompositionEvent('compositionupdate', text, '');
}
domWindowUtils.sendTextEvent(text, 0, 0, 0, 0, 0, 0, 0, 0);
domWindowUtils.sendCompositionEvent('compositionend', text, '');
this._text = '';
this._isStarted = false;
},
// Composition ends due to external actions.
onCompositionEnd: function cm_onCompositionEnd() {
if (!this._isStarted) {
return;
}
this._text = '';
this._isStarted = false;
}
};

View File

@ -23,7 +23,8 @@ let Keyboard = {
'SetValue', 'RemoveFocus', 'SetSelectedOption', 'SetSelectedOptions',
'SetSelectionRange', 'ReplaceSurroundingText', 'ShowInputMethodPicker',
'SwitchToNextInputMethod', 'HideInputMethod',
'GetText', 'SendKey', 'GetContext'
'GetText', 'SendKey', 'GetContext',
'SetComposition', 'EndComposition'
],
get messageManager() {
@ -66,6 +67,8 @@ let Keyboard = {
mm.addMessageListener('Forms:SendKey:Result:OK', this);
mm.addMessageListener('Forms:SequenceError', this);
mm.addMessageListener('Forms:GetContext:Result:OK', this);
mm.addMessageListener('Forms:SetComposition:Result:OK', this);
mm.addMessageListener('Forms:EndComposition:Result:OK', this);
// When not running apps OOP, we need to load forms.js here since this
// won't happen from dom/ipc/preload.js
@ -116,6 +119,8 @@ let Keyboard = {
case 'Forms:SendKey:Result:OK':
case 'Forms:SequenceError':
case 'Forms:GetContext:Result:OK':
case 'Forms:SetComposition:Result:OK':
case 'Forms:EndComposition:Result:OK':
let name = msg.name.replace(/^Forms/, 'Keyboard');
this.forwardEvent(name, msg);
break;
@ -153,6 +158,12 @@ let Keyboard = {
case 'Keyboard:GetContext':
this.getContext(msg);
break;
case 'Keyboard:SetComposition':
this.setComposition(msg);
break;
case 'Keyboard:EndComposition':
this.endComposition(msg);
break;
}
},
@ -223,6 +234,14 @@ let Keyboard = {
getContext: function keyboardGetContext(msg) {
this.messageManager.sendAsyncMessage('Forms:GetContext', msg.data);
},
setComposition: function keyboardSetComposition(msg) {
this.messageManager.sendAsyncMessage('Forms:SetComposition', msg.data);
},
endComposition: function keyboardEndComposition(msg) {
this.messageManager.sendAsyncMessage('Forms:EndComposition', msg.data);
}
};

View File

@ -415,6 +415,8 @@ MozInputContext.prototype = {
"Keyboard:SetSelectionRange:Result:OK",
"Keyboard:ReplaceSurroundingText:Result:OK",
"Keyboard:SendKey:Result:OK",
"Keyboard:SetComposition:Result:OK",
"Keyboard:EndComposition:Result:OK",
"Keyboard:SequenceError"]);
},
@ -472,6 +474,10 @@ MozInputContext.prototype = {
// not invalidated yet...
resolver.reject("InputContext has expired");
break;
case "Keyboard:SetComposition:Result:OK": // Fall through.
case "Keyboard:EndComposition:Result:OK":
resolver.resolve();
break;
default:
dump("Could not find a handler for " + msg.name);
resolver.reject();
@ -627,12 +633,30 @@ MozInputContext.prototype = {
});
},
setComposition: function ic_setComposition(text, cursor) {
throw new this._window.DOMError("NotSupportedError", "Not implemented");
setComposition: function ic_setComposition(text, cursor, clauses) {
let self = this;
return this.createPromise(function(resolver) {
let resolverId = self.getPromiseResolverId(resolver);
cpmm.sendAsyncMessage('Keyboard:SetComposition', {
contextId: self._contextId,
requestId: resolverId,
text: text,
cursor: cursor || text.length,
clauses: clauses || null
});
});
},
endComposition: function ic_endComposition(text) {
throw new this._window.DOMError("NotSupportedError", "Not implemented");
let self = this;
return this.createPromise(function(resolver) {
let resolverId = self.getPromiseResolverId(resolver);
cpmm.sendAsyncMessage('Keyboard:EndComposition', {
contextId: self._contextId,
requestId: resolverId,
text: text || ''
});
});
}
};

View File

@ -1,4 +1,4 @@
{
"revision": "9adc8de2a121d92d43a4669c4326695fe703eb71",
"revision": "b3e578bc32cefba0e0ee997a945898a7c7b9024d",
"repo_path": "/integration/gaia-central"
}

View File

@ -263,9 +263,8 @@ MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS, bool aPrepareForCC)
int32_t i, historyCount;
history->GetCount(&historyCount);
for (i = 0; i < historyCount; ++i) {
nsCOMPtr<nsIHistoryEntry> historyEntry;
history->GetEntryAtIndex(i, false, getter_AddRefs(historyEntry));
nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(historyEntry);
nsCOMPtr<nsISHEntry> shEntry;
history->GetEntryAtIndex(i, false, getter_AddRefs(shEntry));
MarkSHEntry(shEntry, aCleanupJS, aPrepareForCC);
}

View File

@ -89,10 +89,14 @@ public:
NS_IMETHODIMP Run()
{
MOZ_ASSERT(NS_IsMainThread());
mRecorder->mState = RecordingState::Inactive;
mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
mRecorder->mReadThread->Shutdown();
mRecorder->mReadThread = nullptr;
// Setting mState to Inactive here is for the case where SourceStream
// ends itself, thus the recorder should stop itself too.
mRecorder->mState = RecordingState::Inactive;
mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
return NS_OK;
}
@ -154,7 +158,7 @@ MediaRecorder::ExtractEncodedData()
NS_DispatchToMainThread(new PushBlobTask(this));
lastBlobTimeStamp = TimeStamp::Now();
}
} while (mState == RecordingState::Recording && !mEncoder->IsShutdown());
} while (!mEncoder->IsShutdown());
NS_DispatchToMainThread(new PushBlobTask(this));
}
@ -229,7 +233,12 @@ MediaRecorder::Stop(ErrorResult& aResult)
return;
}
mState = RecordingState::Inactive;
mTrackUnionStream->RemoveListener(mEncoder);
mStreamPort->Destroy();
mStreamPort = nullptr;
mTrackUnionStream->Destroy();
mTrackUnionStream = nullptr;
}
void

View File

@ -82,7 +82,7 @@ AudioTrackEncoder::NotifyEndOfStream()
// If source audio chunks are completely silent till the end of encoding,
// initialize the encoder with default channel counts and sampling rate, and
// append this many null data to the segment of track encoder.
if (!mCanceled && !mInitialized && mSilentDuration > 0) {
if (!mCanceled && !mInitialized) {
Init(DEFAULT_CHANNELS, DEFAULT_SAMPLING_RATE);
mRawSegment->AppendNullData(mSilentDuration);
mSilentDuration = 0;

View File

@ -127,6 +127,7 @@ MOCHITEST_FILES = \
test_decoder_disable.html \
test_mediarecorder_record_no_timeslice.html \
test_mediarecorder_reload_crash.html \
test_mediarecorder_record_immediate_stop.html \
test_media_selection.html \
test_playback.html \
test_seekLies.html \

View File

@ -0,0 +1,113 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test MediaRecorder Immediate Stop</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var manager = new MediaTestManager;
/**
* Stops the media recorder immediately after starting the recorder. This test
* verifies whether the media recorder can handle this scenario nicely. The
* return blob size should be greater than zero, but its duration would be zero
* length when play.
*/
function startTest(test, token) {
var element = document.createElement('audio');
var expectedMimeType = test.type.substring(0, test.type.indexOf(';'));
element.token = token;
manager.started(token);
element.src = test.name;
element.test = test;
element.stream = element.mozCaptureStreamUntilEnded();
var mediaRecorder = new MediaRecorder(element.stream);
var onStopFired = false;
var onDataAvailableFired = false;
mediaRecorder.onerror = function () {
ok(false, 'Unexpected onerror callback fired');
};
mediaRecorder.onwarning = function () {
ok(false, 'Unexpected onwarning callback fired');
};
// This handler verifies that only a single onstop event handler is fired.
mediaRecorder.onstop = function () {
if (onStopFired) {
ok(false, 'onstop unexpectedly fired more than once');
} else {
onStopFired = true;
// ondataavailable should always fire before onstop
if (onDataAvailableFired) {
manager.finished(token);
} else {
ok(false, 'onstop fired without an ondataavailable event first');
}
}
};
// This handler verifies that only a single ondataavailable event handler
// is fired with the blob generated having greater than zero size
// and a correct mime type.
mediaRecorder.ondataavailable = function (evt) {
if (onDataAvailableFired) {
ok(false, 'ondataavailable unexpectedly fired more than once');
} else {
onDataAvailableFired = true;
ok(evt instanceof BlobEvent,
'Events fired from ondataavailable should be BlobEvent');
is(evt.type, 'dataavailable',
'Event type should dataavailable');
ok(evt.data.size > 0,
'Blob data received should be greater than zero');
is(evt.data.type, expectedMimeType,
'Blob data received should have type = ' + expectedMimeType);
is(mediaRecorder.mimeType, expectedMimeType,
'Mime type in ondataavailable = ' + expectedMimeType);
// onstop should not have fired before ondataavailable
if (onStopFired) {
ok(false, 'ondataavailable unexpectedly fired later than onstop');
manager.finished(token);
}
}
};
// This handler completes a start and stop of recording and verifies
// respective media recorder state.
var canPlayThrough = function() {
element.removeEventListener('canplaythrough', canPlayThrough, false);
mediaRecorder.start();
is(mediaRecorder.state, 'recording', 'Media recorder should be recording');
is(mediaRecorder.stream, element.stream,
'Media recorder stream = element stream at the start of recording');
mediaRecorder.stop();
is(mediaRecorder.state, 'inactive',
'Media recorder is inactive after being stopped');
is(mediaRecorder.stream, element.stream,
'Media recorder stream = element stream post recording');
};
element.addEventListener('canplaythrough', canPlayThrough, false);
element.play();
}
manager.runTests(gMediaRecorderTests, startTest);
</script>
</pre>
</body>
</html>

View File

@ -117,7 +117,7 @@
#include "nsISHistoryInternal.h"
#include "nsIPrincipal.h"
#include "nsIFileURL.h"
#include "nsIHistoryEntry.h"
#include "nsISHEntry.h"
#include "nsISHistoryListener.h"
#include "nsIWindowWatcher.h"
#include "nsIPromptFactory.h"
@ -3783,7 +3783,7 @@ nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
* a new entry.
*/
int32_t index = -1;
nsCOMPtr<nsIHistoryEntry> currentHE;
nsCOMPtr<nsISHEntry> currentHE;
mSessionHistory->GetIndex(&index);
if (index < 0)
return NS_ERROR_FAILURE;
@ -8049,11 +8049,8 @@ nsDocShell::CreateContentViewer(const char *aContentType,
mSessionHistory->GetRequestedIndex(&idx);
if (idx == -1)
mSessionHistory->GetIndex(&idx);
nsCOMPtr<nsIHistoryEntry> entry;
mSessionHistory->GetEntryAtIndex(idx, false,
getter_AddRefs(entry));
mLSHE = do_QueryInterface(entry);
getter_AddRefs(mLSHE));
}
mLoadType = LOAD_ERROR_PAGE;
@ -9155,13 +9152,11 @@ nsDocShell::InternalLoad(nsIURI * aURI,
if (mSessionHistory) {
int32_t index = -1;
mSessionHistory->GetIndex(&index);
nsCOMPtr<nsIHistoryEntry> hEntry;
nsCOMPtr<nsISHEntry> shEntry;
mSessionHistory->GetEntryAtIndex(index, false,
getter_AddRefs(hEntry));
NS_ENSURE_TRUE(hEntry, NS_ERROR_FAILURE);
nsCOMPtr<nsISHEntry> shEntry(do_QueryInterface(hEntry));
if (shEntry)
shEntry->SetTitle(mTitle);
getter_AddRefs(shEntry));
NS_ENSURE_TRUE(shEntry, NS_ERROR_FAILURE);
shEntry->SetTitle(mTitle);
}
/* Set the title for the Global History entry for this anchor url.

View File

@ -122,7 +122,6 @@ NS_DEFINE_NAMED_CID(NS_EXTERNALSHARINGAPPSERVICE_CID);
NS_DEFINE_NAMED_CID(NS_EXTERNALURLHANDLERSERVICE_CID);
#endif
NS_DEFINE_NAMED_CID(NS_SHENTRY_CID);
NS_DEFINE_NAMED_CID(NS_HISTORYENTRY_CID);
NS_DEFINE_NAMED_CID(NS_SHTRANSACTION_CID);
NS_DEFINE_NAMED_CID(NS_SHISTORY_CID);
NS_DEFINE_NAMED_CID(NS_SHISTORY_INTERNAL_CID);
@ -152,7 +151,6 @@ const mozilla::Module::CIDEntry kDocShellCIDs[] = {
{ &kNS_EXTERNALURLHANDLERSERVICE_CID, false, nullptr, nsExternalURLHandlerServiceConstructor },
#endif
{ &kNS_SHENTRY_CID, false, nullptr, nsSHEntryConstructor },
{ &kNS_HISTORYENTRY_CID, false, nullptr, nsSHEntryConstructor },
{ &kNS_SHTRANSACTION_CID, false, nullptr, nsSHTransactionConstructor },
{ &kNS_SHISTORY_CID, false, nullptr, nsSHistoryConstructor },
{ &kNS_SHISTORY_INTERNAL_CID, false, nullptr, nsSHistoryConstructor },
@ -205,7 +203,6 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
{ NS_EXTERNALURLHANDLERSERVICE_CONTRACTID, &kNS_EXTERNALURLHANDLERSERVICE_CID },
#endif
{ NS_SHENTRY_CONTRACTID, &kNS_SHENTRY_CID },
{ NS_HISTORYENTRY_CONTRACTID, &kNS_HISTORYENTRY_CID },
{ NS_SHTRANSACTION_CONTRACTID, &kNS_SHTRANSACTION_CID },
{ NS_SHISTORY_CONTRACTID, &kNS_SHISTORY_CID },
{ NS_SHISTORY_INTERNAL_CONTRACTID, &kNS_SHISTORY_INTERNAL_CID },

View File

@ -6,7 +6,6 @@
XPIDL_SOURCES += [
'nsIBFCacheEntry.idl',
'nsIHistoryEntry.idl',
'nsISHContainer.idl',
'nsISHEntry.idl',
'nsISHTransaction.idl',

View File

@ -1,58 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
/**
* An interface to individual entries in session history. Each
* document or frame will have a nsIHistoryEntry associated with
* it. nsIHistoryEntry provides access to information like URI,
* title and frame traversal status for that document.
* This interface is accessible from javascript.
*/
#include "nsISupports.idl"
interface nsIURI;
[scriptable, uuid(A41661D4-1417-11D5-9882-00C04FA02F40)]
interface nsIHistoryEntry : nsISupports
{
/**
* A readonly property that returns the URI
* of the current entry. The object returned is
* of type nsIURI
*/
readonly attribute nsIURI URI;
/**
* A readonly property that returns the title
* of the current entry. The object returned
* is a encoded string
*/
readonly attribute wstring title;
/**
* A readonly property that returns a boolean
* flag which indicates if the entry was created as a
* result of a subframe navigation. This flag will be
* 'false' when a frameset page is visited for
* the first time. This flag will be 'true' for all
* history entries created as a result of a subframe
* navigation.
*/
readonly attribute boolean isSubFrame;
};
%{ C++
// {A41661D5-1417-11D5-9882-00C04FA02F40}
#define NS_HISTORYENTRY_CID \
{0xa41661d5, 0x1417, 0x11d5, {0x98, 0x82, 0x0, 0xc0, 0x4f, 0xa0, 0x2f, 0x40}}
#define NS_HISTORYENTRY_CONTRACTID \
"@mozilla.org/browser/history-entry;1"
%}

View File

@ -9,7 +9,8 @@
* hold all information required to recreate the document from history
*
*/
#include "nsIHistoryEntry.idl"
#include "nsISupports.idl"
interface nsILayoutHistoryState;
interface nsIContentViewer;
@ -29,9 +30,34 @@ class nsSHEntryShared;
[ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
[ptr] native nsSHEntryShared(nsSHEntryShared);
[scriptable, uuid(162EA0EB-E577-4B9A-AF9D-A94E8350B029)]
interface nsISHEntry : nsIHistoryEntry
[scriptable, uuid(c2a5827e-0fc0-11e3-bb95-59e799890b3c)]
interface nsISHEntry : nsISupports
{
/**
* A readonly property that returns the URI
* of the current entry. The object returned is
* of type nsIURI
*/
readonly attribute nsIURI URI;
/**
* A readonly property that returns the title
* of the current entry. The object returned
* is a encoded string
*/
readonly attribute wstring title;
/**
* A readonly property that returns a boolean
* flag which indicates if the entry was created as a
* result of a subframe navigation. This flag will be
* 'false' when a frameset page is visited for
* the first time. This flag will be 'true' for all
* history entries created as a result of a subframe
* navigation.
*/
readonly attribute boolean isSubFrame;
/** URI for the document */
void setURI(in nsIURI aURI);

View File

@ -5,7 +5,7 @@
#include "nsISupports.idl"
interface nsIHistoryEntry;
interface nsISHEntry;
interface nsISHistoryListener;
interface nsISimpleEnumerator;
/**
@ -27,7 +27,7 @@ interface nsISimpleEnumerator;
#define NS_SHISTORY_CONTRACTID "@mozilla.org/browser/shistory;1"
%}
[scriptable, uuid(ef2c9bcb-96b8-4095-933a-cb1c506f2c58)]
[scriptable, uuid(b4440e2e-0fc2-11e3-971f-59e799890b3c)]
interface nsISHistory: nsISupports
{
/**
@ -72,7 +72,7 @@ interface nsISHistory: nsISupports
* <code>NS_ERROR_FAILURE</code> Error in obtaining
* history entry for the given index.
*/
nsIHistoryEntry getEntryAtIndex(in long index, in boolean modifyIndex);
nsISHEntry getEntryAtIndex(in long index, in boolean modifyIndex);
/**
@ -135,11 +135,11 @@ interface nsISHistory: nsISupports
* by step #1 to obtain handle to the next object in the list.
* The object returned by this step is of type nsISupports.
* 3) Perform a QueryInterface on the object returned by step #2
* to nsIHistoryEntry.
* 4) Use nsIHistoryEntry to access properties of each history entry.
* to nsISHEntry.
* 4) Use nsISHEntry to access properties of each history entry.
*
* @see nsISimpleEnumerator
* @see nsIHistoryEntry
* @see nsISHEntry
* @see QueryInterface()
* @see do_QueryInterface()
*/

View File

@ -77,8 +77,7 @@ nsSHEntry::~nsSHEntry()
// nsSHEntry: nsISupports
//*****************************************************************************
NS_IMPL_ISUPPORTS4(nsSHEntry, nsISHContainer, nsISHEntry, nsIHistoryEntry,
nsISHEntryInternal)
NS_IMPL_ISUPPORTS3(nsSHEntry, nsISHContainer, nsISHEntry, nsISHEntryInternal)
//*****************************************************************************
// nsSHEntry: nsISHEntry

View File

@ -19,7 +19,6 @@
#include "nsISHEntry.h"
#include "nsISHContainer.h"
#include "nsIURI.h"
#include "nsIHistoryEntry.h"
class nsSHEntryShared;
@ -32,7 +31,6 @@ public:
nsSHEntry(const nsSHEntry &other);
NS_DECL_ISUPPORTS
NS_DECL_NSIHISTORYENTRY
NS_DECL_NSISHENTRY
NS_DECL_NSISHENTRYINTERNAL
NS_DECL_NSISHCONTAINER

View File

@ -427,17 +427,14 @@ nsSHistory::AddEntry(nsISHEntry * aSHEntry, bool aPersist)
NS_ENSURE_TRUE(txn, NS_ERROR_FAILURE);
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIHistoryEntry> hEntry(do_QueryInterface(aSHEntry));
if (hEntry) {
int32_t currentIndex = mIndex;
hEntry->GetURI(getter_AddRefs(uri));
NOTIFY_LISTENERS(OnHistoryNewEntry, (uri));
int32_t currentIndex = mIndex;
aSHEntry->GetURI(getter_AddRefs(uri));
NOTIFY_LISTENERS(OnHistoryNewEntry, (uri));
// If a listener has changed mIndex, we need to get currentTxn again,
// otherwise we'll be left at an inconsistent state (see bug 320742)
if (currentIndex != mIndex) {
GetTransactionAtIndex(mIndex, getter_AddRefs(currentTxn));
}
// If a listener has changed mIndex, we need to get currentTxn again,
// otherwise we'll be left at an inconsistent state (see bug 320742)
if (currentIndex != mIndex) {
GetTransactionAtIndex(mIndex, getter_AddRefs(currentTxn));
}
// Set the ShEntry and parent for the transaction. setting the
@ -489,6 +486,7 @@ nsSHistory::GetRequestedIndex(int32_t * aResult)
return NS_OK;
}
/* Get the entry at a given index */
NS_IMETHODIMP
nsSHistory::GetEntryAtIndex(int32_t aIndex, bool aModifyIndex, nsISHEntry** aResult)
{
@ -510,20 +508,6 @@ nsSHistory::GetEntryAtIndex(int32_t aIndex, bool aModifyIndex, nsISHEntry** aRes
return rv;
}
/* Get the entry at a given index */
NS_IMETHODIMP
nsSHistory::GetEntryAtIndex(int32_t aIndex, bool aModifyIndex, nsIHistoryEntry** aResult)
{
nsresult rv;
nsCOMPtr<nsISHEntry> shEntry;
rv = GetEntryAtIndex(aIndex, aModifyIndex, getter_AddRefs(shEntry));
if (NS_SUCCEEDED(rv) && shEntry)
rv = CallQueryInterface(shEntry, aResult);
return rv;
}
/* Get the transaction at a given index */
NS_IMETHODIMP
nsSHistory::GetTransactionAtIndex(int32_t aIndex, nsISHTransaction ** aResult)
@ -599,11 +583,8 @@ nsSHistory::PrintHistory()
nsXPIDLString title;
entry->GetLayoutHistoryState(getter_AddRefs(layoutHistoryState));
nsCOMPtr<nsIHistoryEntry> hEntry(do_QueryInterface(entry));
if (hEntry) {
hEntry->GetURI(getter_AddRefs(uri));
hEntry->GetTitle(getter_Copies(title));
}
entry->GetURI(getter_AddRefs(uri));
entry->GetTitle(getter_Copies(title));
#if 0
nsAutoCString url;
@ -1256,7 +1237,7 @@ RemoveFromSessionHistoryContainer(nsISHContainer* aContainer,
bool RemoveChildEntries(nsISHistory* aHistory, int32_t aIndex,
nsTArray<uint64_t>& aEntryIDs)
{
nsCOMPtr<nsIHistoryEntry> rootHE;
nsCOMPtr<nsISHEntry> rootHE;
aHistory->GetEntryAtIndex(aIndex, false, getter_AddRefs(rootHE));
nsCOMPtr<nsISHContainer> root = do_QueryInterface(rootHE);
return root ? RemoveFromSessionHistoryContainer(root, aEntryIDs) : false;
@ -1304,11 +1285,9 @@ nsSHistory::RemoveDuplicate(int32_t aIndex, bool aKeepNext)
"If we're removing index 0 we must be keeping the next");
NS_ASSERTION(aIndex != mIndex, "Shouldn't remove mIndex!");
int32_t compareIndex = aKeepNext ? aIndex + 1 : aIndex - 1;
nsCOMPtr<nsIHistoryEntry> rootHE1, rootHE2;
GetEntryAtIndex(aIndex, false, getter_AddRefs(rootHE1));
GetEntryAtIndex(compareIndex, false, getter_AddRefs(rootHE2));
nsCOMPtr<nsISHEntry> root1 = do_QueryInterface(rootHE1);
nsCOMPtr<nsISHEntry> root2 = do_QueryInterface(rootHE2);
nsCOMPtr<nsISHEntry> root1, root2;
GetEntryAtIndex(aIndex, false, getter_AddRefs(root1));
GetEntryAtIndex(compareIndex, false, getter_AddRefs(root2));
if (IsSameTree(root1, root2)) {
nsCOMPtr<nsISHTransaction> txToRemove, txToKeep, txNext, txPrev;
GetTransactionAtIndex(aIndex, getter_AddRefs(txToRemove));
@ -1457,7 +1436,7 @@ nsSHistory::GetCurrentURI(nsIURI** aResultURI)
NS_ENSURE_ARG_POINTER(aResultURI);
nsresult rv;
nsCOMPtr<nsIHistoryEntry> currentEntry;
nsCOMPtr<nsISHEntry> currentEntry;
rv = GetEntryAtIndex(mIndex, false, getter_AddRefs(currentEntry));
if (NS_FAILED(rv) && !currentEntry) return rv;
rv = currentEntry->GetURI(aResultURI);
@ -1531,8 +1510,7 @@ nsSHistory::LoadEntry(int32_t aIndex, long aLoadType, uint32_t aHistCmd)
nsCOMPtr<nsISHEntry> nextEntry;
GetEntryAtIndex(mRequestedIndex, false, getter_AddRefs(nextEntry));
nsCOMPtr<nsIHistoryEntry> nHEntry(do_QueryInterface(nextEntry));
if (!nextEntry || !prevEntry || !nHEntry) {
if (!nextEntry || !prevEntry) {
mRequestedIndex = -1;
return NS_ERROR_FAILURE;
}
@ -1548,7 +1526,7 @@ nsSHistory::LoadEntry(int32_t aIndex, long aLoadType, uint32_t aHistCmd)
bool canNavigate = true;
// Get the uri for the entry we are about to visit
nsCOMPtr<nsIURI> nextURI;
nHEntry->GetURI(getter_AddRefs(nextURI));
nextEntry->GetURI(getter_AddRefs(nextURI));
if (aHistCmd == HIST_CMD_BACK) {
// We are going back one entry. Send GoBack notifications
@ -1753,8 +1731,7 @@ nsSHistory::InitiateLoad(nsISHEntry * aFrameEntry, nsIDocShell * aFrameDS, long
loadInfo->SetSHEntry(aFrameEntry);
nsCOMPtr<nsIURI> nextURI;
nsCOMPtr<nsIHistoryEntry> hEntry(do_QueryInterface(aFrameEntry));
hEntry->GetURI(getter_AddRefs(nextURI));
aFrameEntry->GetURI(getter_AddRefs(nextURI));
// Time to initiate a document load
return aFrameDS->LoadURI(nextURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, false);
@ -1833,7 +1810,7 @@ nsSHEnumerator::GetNext(nsISupports **aItem)
mSHistory->GetCount(&cnt);
if (mIndex < (cnt-1)) {
mIndex++;
nsCOMPtr<nsIHistoryEntry> hEntry;
nsCOMPtr<nsISHEntry> hEntry;
result = mSHistory->GetEntryAtIndex(mIndex, false, getter_AddRefs(hEntry));
if (hEntry)
result = CallQueryInterface(hEntry, aItem);

View File

@ -18,7 +18,7 @@
#include "nsIWeakReference.h"
#include "nsISimpleEnumerator.h"
#include "nsISHistoryListener.h"
#include "nsIHistoryEntry.h"
#include "nsISHEntry.h"
#include "nsTObserverArray.h"
// Needed to maintain global list of all SHistory objects
@ -57,7 +57,6 @@ protected:
friend class nsSHistoryObserver;
// Could become part of nsIWebNavigation
NS_IMETHOD GetEntryAtIndex(int32_t aIndex, bool aModifyIndex, nsISHEntry** aResult);
NS_IMETHOD GetTransactionAtIndex(int32_t aIndex, nsISHTransaction ** aResult);
nsresult CompareFrames(nsISHEntry * prevEntry, nsISHEntry * nextEntry, nsIDocShell * rootDocShell, long aLoadType, bool * aIsFrameFound);
nsresult InitiateLoad(nsISHEntry * aFrameEntry, nsIDocShell * aFrameDS, long aLoadType);

View File

@ -184,8 +184,12 @@ function xpcWaitForFinishedFrames(callback, numFrames) {
(win.document.body.textContent == body ||
win.document.body.textContent == popup_body) &&
win.document.readyState == "complete") {
if (!contains(win, finishedWindows)) {
finishedWindows.push(win);
var util = win.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
.getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
var windowId = util.outerWindowID;
if (!contains(windowId, finishedWindows)) {
finishedWindows.push(windowId);
frameFinished();
}
}

View File

@ -13,7 +13,6 @@
#include "nsPresContext.h"
#include "nsIDocShell.h"
#include "nsIWebNavigation.h"
#include "nsIHistoryEntry.h"
#include "nsIURI.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsReadableUtils.h"

View File

@ -146,24 +146,59 @@ interface MozInputContext: EventTarget {
Promise sendKey(long keyCode, long charCode, long modifiers);
/*
* Set current composition. It will start or update composition.
* @param cursor Position in the text of the cursor.
* Set current composing text. This method will start composition or update
* composition if it has started. The composition will be started right
* before the cursor position and any selected text will be replaced by the
* composing text. When the composition is started, calling this method can
* update the text and move cursor winthin the range of the composing text.
* @param text The new composition text to show.
* @param cursor The new cursor position relative to the start of the
* composition text. The cursor should be positioned within the composition
* text. This means the value should be >= 0 and <= the length of
* composition text. Defaults to the lenght of composition text, i.e., the
* cursor will be positioned after the composition text.
* @param clauses The array of composition clause information. If not set,
* only one clause is supported.
*
* The API implementation should automatically ends the composition
* session (with event and confirm the current composition) if
* endComposition is never called. Same apply when the inputContext is lost
* during a unfinished composition session.
* The composing text, which is shown with underlined style to distinguish
* from the existing text, is used to compose non-ASCII characters from
* keystrokes, e.g. Pinyin or Hiragana. The composing text is the
* intermediate text to help input complex character and is not committed to
* current input field. Therefore if any text operation other than
* composition is performed, the composition will automatically end. Same
* apply when the inputContext is lost during an unfinished composition
* session.
*
* To finish composition and commit text to current input field, an IME
* should call |endComposition|.
*/
Promise setComposition(DOMString text, long cursor);
Promise setComposition(DOMString text, optional long cursor,
optional sequence<CompositionClauseParameters> clauses);
/*
* End composition and actually commit the text. (was |commitText(text, offset, length)|)
* Ending the composition with an empty string will not send any text.
* Note that if composition always ends automatically (with the current composition committed)
* if the composition did not explicitly with |endComposition()| but was interrupted with
* |sendKey()|, |setSelectionRange()|, user moving the cursor, or remove the focus, etc.
* End composition, clear the composing text and commit given text to
* current input field. The text will be committed before the cursor
* position.
* @param text The text to commited before cursor position. If empty string
* is given, no text will be committed.
*
* @param text The text
* Note that composition always ends automatically with nothing to commit if
* the composition does not explicitly end by calling |endComposition|, but
* is interrupted by |sendKey|, |setSelectionRange|,
* |replaceSurroundingText|, |deleteSurroundingText|, user moving the
* cursor, changing the focus, etc.
*/
Promise endComposition(DOMString text);
Promise endComposition(optional DOMString text);
};
enum CompositionClauseSelectionType {
"raw-input",
"selected-raw-text",
"converted-text",
"selected-converted-text"
};
dictionary CompositionClauseParameters {
DOMString selectionType = "raw-input";
long length;
};

View File

@ -128,7 +128,7 @@ GrallocTextureClientOGL::AllocateForSurface(gfx::IntSize aSize)
}
bool
GrallocTextureClientOGL::AllocateForYCbCr(gfx::IntSize aYSize, gfx::IntSize aCbCrSize)
GrallocTextureClientOGL::AllocateForYCbCr(gfx::IntSize aYSize, gfx::IntSize aCbCrSize, StereoMode aStereoMode)
{
return AllocateGralloc(aYSize,
HAL_PIXEL_FORMAT_YV12,

View File

@ -78,7 +78,9 @@ public:
virtual bool AllocateForSurface(gfx::IntSize aSize) MOZ_OVERRIDE;
virtual bool AllocateForYCbCr(gfx::IntSize aYSize, gfx::IntSize aCbCrSize) MOZ_OVERRIDE;
virtual bool AllocateForYCbCr(gfx::IntSize aYSize,
gfx::IntSize aCbCrSize,
StereoMode aStereoMode) MOZ_OVERRIDE;
bool AllocateGralloc(gfx::IntSize aYSize, uint32_t aAndroidFormat, uint32_t aUsage);
@ -116,4 +118,4 @@ protected:
} // namespace mozilla
#endif // MOZ_WIDGET_GONK
#endif
#endif

View File

@ -143,7 +143,7 @@ GrallocTextureSourceOGL::GetTextureTarget() const
}
gfx::SurfaceFormat
GrallocTextureSourceOGL::GetFormat() const MOZ_OVERRIDE {
GrallocTextureSourceOGL::GetFormat() const {
if (!mGraphicBuffer.get()) {
return gfx::FORMAT_UNKNOWN;
}

View File

@ -26,23 +26,25 @@ public:
android::GraphicBuffer* aGraphicBuffer,
gfx::SurfaceFormat aFormat);
virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; }
virtual bool IsValid() const MOZ_OVERRIDE;
virtual void BindTexture(GLenum aTextureUnit) MOZ_OVERRIDE;
virtual void UnbindTexture() MOZ_OVERRIDE {}
virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE;
virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; }
virtual GLenum GetTextureTarget() const MOZ_OVERRIDE;
virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE;
virtual GLenum GetWrapMode() const MOZ_OVERRIDE
{
return LOCAL_GL_CLAMP_TO_EDGE;
}
virtual void UnbindTexture() MOZ_OVERRIDE {}
void DeallocateDeviceData();
gl::GLContext* gl() const;
@ -57,8 +59,6 @@ public:
mGraphicBuffer = nullptr;
}
bool IsValid() const;
already_AddRefed<gfxImageSurface> GetAsSurface();
protected:
@ -84,8 +84,6 @@ public:
virtual void Unlock() MOZ_OVERRIDE;
virtual bool IsValid() const MOZ_OVERRIDE;
virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
virtual void DeallocateSharedData() MOZ_OVERRIDE;
@ -105,6 +103,8 @@ public:
virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE;
bool IsValid() const;
#ifdef MOZ_LAYERS_HAVE_LOG
virtual const char* Name() { return "GrallocTextureHostOGL"; }
#endif