mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound
This commit is contained in:
commit
10df022340
@ -4,7 +4,7 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
* 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/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
"use strict;"
|
"use strict";
|
||||||
|
|
||||||
const Cc = Components.classes;
|
const Cc = Components.classes;
|
||||||
const Ci = Components.interfaces;
|
const Ci = Components.interfaces;
|
||||||
@ -21,47 +21,65 @@ XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
|
|||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Once Bug 731746 - Allow chrome JS object to implement nsIDOMEventTarget
|
|
||||||
// is resolved this helper could be removed.
|
|
||||||
var SettingsListener = {
|
var SettingsListener = {
|
||||||
_callbacks: {},
|
// Timer to remove the lock.
|
||||||
|
_timer: null,
|
||||||
|
|
||||||
init: function sl_init() {
|
// lock stores here
|
||||||
if ('mozSettings' in navigator && navigator.mozSettings) {
|
_lock: null,
|
||||||
navigator.mozSettings.onsettingchange = this.onchange.bind(this);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onchange: function sl_onchange(evt) {
|
/**
|
||||||
var callback = this._callbacks[evt.settingName];
|
* getSettingsLock: create a lock or retrieve one that we saved.
|
||||||
if (callback) {
|
* mozSettings.createLock() is expensive and lock should be reused
|
||||||
callback(evt.settingValue);
|
* whenever possible.
|
||||||
|
*/
|
||||||
|
getSettingsLock: function sl_getSettingsLock() {
|
||||||
|
// Each time there is a getSettingsLock call, we postpone the removal.
|
||||||
|
clearTimeout(this._timer);
|
||||||
|
this._timer = setTimeout((function() {
|
||||||
|
this._lock = null;
|
||||||
|
}).bind(this), 0);
|
||||||
|
|
||||||
|
// If there is a lock present we return that.
|
||||||
|
if (this._lock) {
|
||||||
|
return this._lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there isn't we create a new one.
|
||||||
|
let settings = window.navigator.mozSettings;
|
||||||
|
|
||||||
|
return (this._lock = settings.createLock());
|
||||||
},
|
},
|
||||||
|
|
||||||
observe: function sl_observe(name, defaultValue, callback) {
|
observe: function sl_observe(name, defaultValue, callback) {
|
||||||
var settings = window.navigator.mozSettings;
|
let settings = window.navigator.mozSettings;
|
||||||
if (!settings) {
|
|
||||||
window.setTimeout(function() { callback(defaultValue); });
|
let req;
|
||||||
return;
|
try {
|
||||||
|
req = this.getSettingsLock().get(name);
|
||||||
|
} catch (e) {
|
||||||
|
// It is possible (but rare) for getSettingsLock() to return
|
||||||
|
// a SettingsLock object that is no longer valid.
|
||||||
|
// Until https://bugzilla.mozilla.org/show_bug.cgi?id=793239
|
||||||
|
// is fixed, we just catch the resulting exception and try
|
||||||
|
// again with a fresh lock
|
||||||
|
console.warn('Stale lock in settings.js.',
|
||||||
|
'See https://bugzilla.mozilla.org/show_bug.cgi?id=793239');
|
||||||
|
this._lock = null;
|
||||||
|
req = this.getSettingsLock().get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!callback || typeof callback !== 'function') {
|
|
||||||
throw new Error('Callback is not a function');
|
|
||||||
}
|
|
||||||
|
|
||||||
var req = settings.createLock().get(name);
|
|
||||||
req.addEventListener('success', (function onsuccess() {
|
req.addEventListener('success', (function onsuccess() {
|
||||||
callback(typeof(req.result[name]) != 'undefined' ?
|
callback(typeof(req.result[name]) != 'undefined' ?
|
||||||
req.result[name] : defaultValue);
|
req.result[name] : defaultValue);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._callbacks[name] = callback;
|
settings.addObserver(name, function settingChanged(evt) {
|
||||||
|
callback(evt.settingValue);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SettingsListener.init();
|
|
||||||
|
|
||||||
// =================== Console ======================
|
// =================== Console ======================
|
||||||
|
|
||||||
SettingsListener.observe('debug.console.enabled', true, function(value) {
|
SettingsListener.observe('debug.console.enabled', true, function(value) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"revision": "752ced5b3cc3208f79806eccf8d8768910f17f2b",
|
"revision": "4296bbf526e4ed8d0ae443f20947bd2d7189aa0e",
|
||||||
"repo_path": "/integration/gaia-central"
|
"repo_path": "/integration/gaia-central"
|
||||||
}
|
}
|
||||||
|
@ -1079,6 +1079,7 @@ pref("devtools.inspector.enabled", true);
|
|||||||
pref("devtools.inspector.activeSidebar", "ruleview");
|
pref("devtools.inspector.activeSidebar", "ruleview");
|
||||||
pref("devtools.inspector.markupPreview", false);
|
pref("devtools.inspector.markupPreview", false);
|
||||||
pref("devtools.inspector.remote", false);
|
pref("devtools.inspector.remote", false);
|
||||||
|
pref("devtools.inspector.show_pseudo_elements", true);
|
||||||
|
|
||||||
// Enable the Layout View
|
// Enable the Layout View
|
||||||
pref("devtools.layoutview.enabled", true);
|
pref("devtools.layoutview.enabled", true);
|
||||||
|
@ -37,6 +37,10 @@ SocialUI = {
|
|||||||
Services.obs.addObserver(this, "social:provider-set", false);
|
Services.obs.addObserver(this, "social:provider-set", false);
|
||||||
Services.obs.addObserver(this, "social:providers-changed", false);
|
Services.obs.addObserver(this, "social:providers-changed", false);
|
||||||
Services.obs.addObserver(this, "social:provider-reload", false);
|
Services.obs.addObserver(this, "social:provider-reload", false);
|
||||||
|
Services.obs.addObserver(this, "social:provider-installed", false);
|
||||||
|
Services.obs.addObserver(this, "social:provider-uninstalled", false);
|
||||||
|
Services.obs.addObserver(this, "social:provider-enabled", false);
|
||||||
|
Services.obs.addObserver(this, "social:provider-disabled", false);
|
||||||
|
|
||||||
Services.prefs.addObserver("social.sidebar.open", this, false);
|
Services.prefs.addObserver("social.sidebar.open", this, false);
|
||||||
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
|
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
|
||||||
@ -62,6 +66,10 @@ SocialUI = {
|
|||||||
Services.obs.removeObserver(this, "social:provider-set");
|
Services.obs.removeObserver(this, "social:provider-set");
|
||||||
Services.obs.removeObserver(this, "social:providers-changed");
|
Services.obs.removeObserver(this, "social:providers-changed");
|
||||||
Services.obs.removeObserver(this, "social:provider-reload");
|
Services.obs.removeObserver(this, "social:provider-reload");
|
||||||
|
Services.obs.removeObserver(this, "social:provider-installed");
|
||||||
|
Services.obs.removeObserver(this, "social:provider-uninstalled");
|
||||||
|
Services.obs.removeObserver(this, "social:provider-enabled");
|
||||||
|
Services.obs.removeObserver(this, "social:provider-disabled");
|
||||||
|
|
||||||
Services.prefs.removeObserver("social.sidebar.open", this);
|
Services.prefs.removeObserver("social.sidebar.open", this);
|
||||||
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
|
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
|
||||||
@ -76,6 +84,18 @@ SocialUI = {
|
|||||||
// manually :(
|
// manually :(
|
||||||
try {
|
try {
|
||||||
switch (topic) {
|
switch (topic) {
|
||||||
|
case "social:provider-installed":
|
||||||
|
SocialStatus.setPosition(data);
|
||||||
|
break;
|
||||||
|
case "social:provider-uninstalled":
|
||||||
|
SocialStatus.removePosition(data);
|
||||||
|
break;
|
||||||
|
case "social:provider-enabled":
|
||||||
|
SocialStatus.populateToolbarPalette();
|
||||||
|
break;
|
||||||
|
case "social:provider-disabled":
|
||||||
|
SocialStatus.removeProvider(data);
|
||||||
|
break;
|
||||||
case "social:provider-reload":
|
case "social:provider-reload":
|
||||||
// if the reloaded provider is our current provider, fall through
|
// if the reloaded provider is our current provider, fall through
|
||||||
// to social:provider-set so the ui will be reset
|
// to social:provider-set so the ui will be reset
|
||||||
@ -98,6 +118,7 @@ SocialUI = {
|
|||||||
SocialSidebar.update();
|
SocialSidebar.update();
|
||||||
SocialMark.update();
|
SocialMark.update();
|
||||||
SocialToolbar.update();
|
SocialToolbar.update();
|
||||||
|
SocialStatus.populateToolbarPalette();
|
||||||
SocialMenu.populate();
|
SocialMenu.populate();
|
||||||
break;
|
break;
|
||||||
case "social:providers-changed":
|
case "social:providers-changed":
|
||||||
@ -106,10 +127,12 @@ SocialUI = {
|
|||||||
// and the multi-provider menu
|
// and the multi-provider menu
|
||||||
SocialToolbar.populateProviderMenus();
|
SocialToolbar.populateProviderMenus();
|
||||||
SocialShare.populateProviderMenu();
|
SocialShare.populateProviderMenu();
|
||||||
|
SocialStatus.populateToolbarPalette();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Provider-specific notifications
|
// Provider-specific notifications
|
||||||
case "social:ambient-notification-changed":
|
case "social:ambient-notification-changed":
|
||||||
|
SocialStatus.updateNotification(data);
|
||||||
if (this._matchesCurrentProvider(data)) {
|
if (this._matchesCurrentProvider(data)) {
|
||||||
SocialToolbar.updateButton();
|
SocialToolbar.updateButton();
|
||||||
SocialMenu.populate();
|
SocialMenu.populate();
|
||||||
@ -1056,6 +1079,15 @@ SocialToolbar = {
|
|||||||
Services.prefs.clearUserPref(CACHE_PREF_NAME);
|
Services.prefs.clearUserPref(CACHE_PREF_NAME);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the provider uses the new SocialStatus button, then they do not get
|
||||||
|
// to use the ambient icons in the old toolbar button. Since the status
|
||||||
|
// button depends on multiple workers, if not enabled we will ignore this
|
||||||
|
// limitation. That allows a provider to migrate to the new functionality
|
||||||
|
// once we enable multiple workers.
|
||||||
|
if (Social.provider.statusURL && Social.allowMultipleWorkers)
|
||||||
|
return;
|
||||||
|
|
||||||
let icons = Social.provider.ambientNotificationIcons;
|
let icons = Social.provider.ambientNotificationIcons;
|
||||||
let iconNames = Object.keys(icons);
|
let iconNames = Object.keys(icons);
|
||||||
|
|
||||||
@ -1154,9 +1186,8 @@ SocialToolbar = {
|
|||||||
socialToolbarItem.insertBefore(toolbarButtons, SocialMark.button);
|
socialToolbarItem.insertBefore(toolbarButtons, SocialMark.button);
|
||||||
|
|
||||||
for (let frame of createdFrames) {
|
for (let frame of createdFrames) {
|
||||||
if (frame.socialErrorListener) {
|
if (frame.socialErrorListener)
|
||||||
frame.socialErrorListener.remove();
|
frame.socialErrorListener.remove();
|
||||||
}
|
|
||||||
if (frame.docShell) {
|
if (frame.docShell) {
|
||||||
frame.docShell.isActive = false;
|
frame.docShell.isActive = false;
|
||||||
Social.setErrorListener(frame, this.setPanelErrorMessage.bind(this));
|
Social.setErrorListener(frame, this.setPanelErrorMessage.bind(this));
|
||||||
@ -1201,11 +1232,10 @@ SocialToolbar = {
|
|||||||
|
|
||||||
panel.addEventListener("popupshown", function onpopupshown() {
|
panel.addEventListener("popupshown", function onpopupshown() {
|
||||||
panel.removeEventListener("popupshown", onpopupshown);
|
panel.removeEventListener("popupshown", onpopupshown);
|
||||||
// This attribute is needed on both the button and the
|
// The "open" attribute is needed on both the button and the containing
|
||||||
// containing toolbaritem since the buttons on OS X have
|
// toolbaritem since the buttons on OS X have moz-appearance:none, while
|
||||||
// moz-appearance:none, while their container gets
|
// their container gets moz-appearance:toolbarbutton due to the way that
|
||||||
// moz-appearance:toolbarbutton due to the way that toolbar buttons
|
// toolbar buttons get combined on OS X.
|
||||||
// get combined on OS X.
|
|
||||||
aToolbarButton.setAttribute("open", "true");
|
aToolbarButton.setAttribute("open", "true");
|
||||||
aToolbarButton.parentNode.setAttribute("open", "true");
|
aToolbarButton.parentNode.setAttribute("open", "true");
|
||||||
notificationFrame.docShell.isActive = true;
|
notificationFrame.docShell.isActive = true;
|
||||||
@ -1392,4 +1422,353 @@ SocialSidebar = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this helper class is used by removable/customizable buttons to handle
|
||||||
|
// location persistence and insertion into palette and/or toolbars
|
||||||
|
|
||||||
|
// When a provider is installed we show all their UI so the user will see the
|
||||||
|
// functionality of what they installed. The user can later customize the UI,
|
||||||
|
// moving buttons around or off the toolbar.
|
||||||
|
//
|
||||||
|
// To make this happen, on install we add a button id to the navbar currentset.
|
||||||
|
// On enabling the provider (happens just after install) we insert the button
|
||||||
|
// into the toolbar as well. The button is then persisted on restart (assuming
|
||||||
|
// it was not removed).
|
||||||
|
//
|
||||||
|
// When a provider is disabled, we do not remove the buttons from currentset.
|
||||||
|
// That way, if the provider is re-enabled during the same session, the buttons
|
||||||
|
// will reappear where they were before. When a provider is uninstalled, we make
|
||||||
|
// sure that the id is removed from currentset.
|
||||||
|
//
|
||||||
|
// On startup, we insert the buttons of any enabled provider into either the
|
||||||
|
// apropriate toolbar or the palette.
|
||||||
|
function ToolbarHelper(type, createButtonFn) {
|
||||||
|
this._createButton = createButtonFn;
|
||||||
|
this._type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolbarHelper.prototype = {
|
||||||
|
idFromOrgin: function(origin) {
|
||||||
|
return this._type + "-" + origin;
|
||||||
|
},
|
||||||
|
|
||||||
|
// find a button either in the document or the palette
|
||||||
|
_getExistingButton: function(id) {
|
||||||
|
let button = document.getElementById(id);
|
||||||
|
if (button)
|
||||||
|
return button;
|
||||||
|
let palette = document.getElementById("navigator-toolbox").palette;
|
||||||
|
let paletteItem = palette.firstChild;
|
||||||
|
while (paletteItem) {
|
||||||
|
if (paletteItem.id == id)
|
||||||
|
return paletteItem;
|
||||||
|
paletteItem = paletteItem.nextSibling;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
setPersistentPosition: function(id) {
|
||||||
|
// called when a provider is installed. add provider buttons to nav-bar
|
||||||
|
let toolbar = document.getElementById("nav-bar");
|
||||||
|
// first startups will not have a currentset attribute, always rely on
|
||||||
|
// currentSet since it will be derived from the defaultset in that case.
|
||||||
|
let currentset = toolbar.currentSet;
|
||||||
|
if (currentset == "__empty")
|
||||||
|
currentset = []
|
||||||
|
else
|
||||||
|
currentset = currentset.split(",");
|
||||||
|
if (currentset.indexOf(id) >= 0)
|
||||||
|
return;
|
||||||
|
// we do not set toolbar.currentSet since that will try to add the button,
|
||||||
|
// and we have not added it yet (happens on provider being enabled)
|
||||||
|
currentset.push(id);
|
||||||
|
toolbar.setAttribute("currentset", currentset.join(","));
|
||||||
|
document.persist(toolbar.id, "currentset");
|
||||||
|
},
|
||||||
|
|
||||||
|
removeProviderButton: function(origin) {
|
||||||
|
// this will remove the button from the palette or the toolbar
|
||||||
|
let button = this._getExistingButton(this.idFromOrgin(origin));
|
||||||
|
if (button)
|
||||||
|
button.parentNode.removeChild(button);
|
||||||
|
},
|
||||||
|
|
||||||
|
removePersistence: function(id) {
|
||||||
|
let persisted = document.querySelectorAll("*[currentset]");
|
||||||
|
for (let pent of persisted) {
|
||||||
|
// the button will have been removed, but left in the currentset attribute
|
||||||
|
// in case the user re-enables (e.g. undo in addon manager). So we only
|
||||||
|
// check the attribute here.
|
||||||
|
let currentset = pent.getAttribute("currentset").split(",");
|
||||||
|
|
||||||
|
let pos = currentset.indexOf(id);
|
||||||
|
if (pos >= 0) {
|
||||||
|
currentset.splice(pos, 1);
|
||||||
|
pent.setAttribute("currentset", currentset.join(","));
|
||||||
|
document.persist(pent.id, "currentset");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// if social is entirely disabled, we need to clear the palette, but leave
|
||||||
|
// the persisted id's in place
|
||||||
|
clearPalette: function() {
|
||||||
|
[this.removeProviderButton(p.origin) for (p of Social.providers)];
|
||||||
|
},
|
||||||
|
|
||||||
|
// should be called on startup of each window, otherwise the addon manager
|
||||||
|
// listener will handle new activations, or enable/disabling of a provider
|
||||||
|
// XXX we currently call more regularly, will fix during refactoring
|
||||||
|
populatePalette: function() {
|
||||||
|
if (!Social.enabled) {
|
||||||
|
this.clearPalette();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let persisted = document.querySelectorAll("*[currentset]");
|
||||||
|
let persistedById = {};
|
||||||
|
for (let pent of persisted) {
|
||||||
|
let pset = pent.getAttribute("currentset").split(',');
|
||||||
|
for (let id of pset)
|
||||||
|
persistedById[id] = pent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create any buttons that do not exist yet if they have been persisted
|
||||||
|
// as a part of the UI (otherwise they belong in the palette).
|
||||||
|
for (let provider of Social.providers) {
|
||||||
|
let id = this.idFromOrgin(provider.origin);
|
||||||
|
if (this._getExistingButton(id))
|
||||||
|
return;
|
||||||
|
let button = this._createButton(provider);
|
||||||
|
if (button && persistedById.hasOwnProperty(id)) {
|
||||||
|
let parent = persistedById[id];
|
||||||
|
let pset = persistedById[id].getAttribute("currentset").split(',');
|
||||||
|
let pi = pset.indexOf(id) + 1;
|
||||||
|
let next = document.getElementById(pset[pi]);
|
||||||
|
parent.insertItem(id, next, null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SocialStatus = {
|
||||||
|
populateToolbarPalette: function() {
|
||||||
|
if (!Social.allowMultipleWorkers)
|
||||||
|
return;
|
||||||
|
this._toolbarHelper.populatePalette();
|
||||||
|
},
|
||||||
|
|
||||||
|
setPosition: function(origin) {
|
||||||
|
if (!Social.allowMultipleWorkers)
|
||||||
|
return;
|
||||||
|
// this is called during install, before the provider is enabled so we have
|
||||||
|
// to use the manifest rather than the provider instance as we do elsewhere.
|
||||||
|
let manifest = Social.getManifestByOrigin(origin);
|
||||||
|
if (!manifest.statusURL)
|
||||||
|
return;
|
||||||
|
let tbh = this._toolbarHelper;
|
||||||
|
tbh.setPersistentPosition(tbh.idFromOrgin(origin));
|
||||||
|
},
|
||||||
|
|
||||||
|
removePosition: function(origin) {
|
||||||
|
if (!Social.allowMultipleWorkers)
|
||||||
|
return;
|
||||||
|
let tbh = this._toolbarHelper;
|
||||||
|
tbh.removePersistence(tbh.idFromOrgin(origin));
|
||||||
|
},
|
||||||
|
|
||||||
|
removeProvider: function(origin) {
|
||||||
|
if (!Social.allowMultipleWorkers)
|
||||||
|
return;
|
||||||
|
this._toolbarHelper.removeProviderButton(origin);
|
||||||
|
},
|
||||||
|
|
||||||
|
get _toolbarHelper() {
|
||||||
|
delete this._toolbarHelper;
|
||||||
|
this._toolbarHelper = new ToolbarHelper("social-status-button", this._createButton.bind(this));
|
||||||
|
return this._toolbarHelper;
|
||||||
|
},
|
||||||
|
|
||||||
|
get _dynamicResizer() {
|
||||||
|
delete this._dynamicResizer;
|
||||||
|
this._dynamicResizer = new DynamicResizeWatcher();
|
||||||
|
return this._dynamicResizer;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createButton: function(provider) {
|
||||||
|
if (!provider.statusURL)
|
||||||
|
return null;
|
||||||
|
let palette = document.getElementById("navigator-toolbox").palette;
|
||||||
|
let button = document.createElement("toolbarbutton");
|
||||||
|
button.setAttribute("class", "toolbarbutton-1 social-status-button");
|
||||||
|
button.setAttribute("type", "badged");
|
||||||
|
button.setAttribute("removable", "true");
|
||||||
|
button.setAttribute("image", provider.iconURL);
|
||||||
|
button.setAttribute("label", provider.name);
|
||||||
|
button.setAttribute("tooltiptext", provider.name);
|
||||||
|
button.setAttribute("origin", provider.origin);
|
||||||
|
button.setAttribute("oncommand", "SocialStatus.showPopup(this);");
|
||||||
|
button.setAttribute("id", this._toolbarHelper.idFromOrgin(provider.origin));
|
||||||
|
palette.appendChild(button);
|
||||||
|
return button;
|
||||||
|
},
|
||||||
|
|
||||||
|
// status panels are one-per button per-process, we swap the docshells between
|
||||||
|
// windows when necessary
|
||||||
|
_attachNotificatonPanel: function(aButton, provider) {
|
||||||
|
let panel = document.getElementById("social-notification-panel");
|
||||||
|
panel.hidden = !SocialUI.enabled;
|
||||||
|
let notificationFrameId = "social-status-" + provider.origin;
|
||||||
|
let frame = document.getElementById(notificationFrameId);
|
||||||
|
|
||||||
|
if (!frame) {
|
||||||
|
frame = SharedFrame.createFrame(
|
||||||
|
notificationFrameId, /* frame name */
|
||||||
|
panel, /* parent */
|
||||||
|
{
|
||||||
|
"type": "content",
|
||||||
|
"mozbrowser": "true",
|
||||||
|
"class": "social-panel-frame",
|
||||||
|
"id": notificationFrameId,
|
||||||
|
"tooltip": "aHTMLTooltip",
|
||||||
|
|
||||||
|
// work around bug 793057 - by making the panel roughly the final size
|
||||||
|
// we are more likely to have the anchor in the correct position.
|
||||||
|
"style": "width: " + PANEL_MIN_WIDTH + "px;",
|
||||||
|
|
||||||
|
"origin": provider.origin,
|
||||||
|
"src": provider.statusURL
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (frame.socialErrorListener)
|
||||||
|
frame.socialErrorListener.remove();
|
||||||
|
if (frame.docShell) {
|
||||||
|
frame.docShell.isActive = false;
|
||||||
|
Social.setErrorListener(frame, this.setPanelErrorMessage.bind(this));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
frame.setAttribute("origin", provider.origin);
|
||||||
|
SharedFrame.updateURL(notificationFrameId, provider.statusURL);
|
||||||
|
}
|
||||||
|
aButton.setAttribute("notificationFrameId", notificationFrameId);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateNotification: function(origin) {
|
||||||
|
if (!Social.allowMultipleWorkers)
|
||||||
|
return;
|
||||||
|
let provider = Social._getProviderFromOrigin(origin);
|
||||||
|
let button = document.getElementById(this._toolbarHelper.idFromOrgin(provider.origin));
|
||||||
|
if (button) {
|
||||||
|
// we only grab the first notification, ignore all others
|
||||||
|
let icons = provider.ambientNotificationIcons;
|
||||||
|
let iconNames = Object.keys(icons);
|
||||||
|
let notif = icons[iconNames[0]];
|
||||||
|
if (!notif) {
|
||||||
|
button.setAttribute("badge", "");
|
||||||
|
button.setAttribute("aria-label", "");
|
||||||
|
button.setAttribute("tooltiptext", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.style.listStyleImage = "url(" + notif.iconURL || provider.iconURL + ")";
|
||||||
|
button.setAttribute("tooltiptext", notif.label);
|
||||||
|
|
||||||
|
let badge = notif.counter || "";
|
||||||
|
button.setAttribute("badge", badge);
|
||||||
|
let ariaLabel = notif.label;
|
||||||
|
// if there is a badge value, we must use a localizable string to insert it.
|
||||||
|
if (badge)
|
||||||
|
ariaLabel = gNavigatorBundle.getFormattedString("social.aria.toolbarButtonBadgeText",
|
||||||
|
[ariaLabel, badge]);
|
||||||
|
button.setAttribute("aria-label", ariaLabel);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showPopup: function(aToolbarButton) {
|
||||||
|
if (!Social.allowMultipleWorkers)
|
||||||
|
return;
|
||||||
|
// attach our notification panel if necessary
|
||||||
|
let origin = aToolbarButton.getAttribute("origin");
|
||||||
|
let provider = Social._getProviderFromOrigin(origin);
|
||||||
|
this._attachNotificatonPanel(aToolbarButton, provider);
|
||||||
|
|
||||||
|
let panel = document.getElementById("social-notification-panel");
|
||||||
|
let notificationFrameId = aToolbarButton.getAttribute("notificationFrameId");
|
||||||
|
let notificationFrame = document.getElementById(notificationFrameId);
|
||||||
|
|
||||||
|
let wasAlive = SharedFrame.isGroupAlive(notificationFrameId);
|
||||||
|
SharedFrame.setOwner(notificationFrameId, notificationFrame);
|
||||||
|
|
||||||
|
// Clear dimensions on all browsers so the panel size will
|
||||||
|
// only use the selected browser.
|
||||||
|
let frameIter = panel.firstElementChild;
|
||||||
|
while (frameIter) {
|
||||||
|
frameIter.collapsed = (frameIter != notificationFrame);
|
||||||
|
frameIter = frameIter.nextElementSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchPanelEvent(name) {
|
||||||
|
let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
|
||||||
|
evt.initCustomEvent(name, true, true, {});
|
||||||
|
notificationFrame.contentDocument.documentElement.dispatchEvent(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dynamicResizer = this._dynamicResizer;
|
||||||
|
panel.addEventListener("popuphidden", function onpopuphiding() {
|
||||||
|
panel.removeEventListener("popuphidden", onpopuphiding);
|
||||||
|
aToolbarButton.removeAttribute("open");
|
||||||
|
dynamicResizer.stop();
|
||||||
|
notificationFrame.docShell.isActive = false;
|
||||||
|
dispatchPanelEvent("socialFrameHide");
|
||||||
|
});
|
||||||
|
|
||||||
|
panel.addEventListener("popupshown", function onpopupshown() {
|
||||||
|
panel.removeEventListener("popupshown", onpopupshown);
|
||||||
|
// This attribute is needed on both the button and the
|
||||||
|
// containing toolbaritem since the buttons on OS X have
|
||||||
|
// moz-appearance:none, while their container gets
|
||||||
|
// moz-appearance:toolbarbutton due to the way that toolbar buttons
|
||||||
|
// get combined on OS X.
|
||||||
|
aToolbarButton.setAttribute("open", "true");
|
||||||
|
notificationFrame.docShell.isActive = true;
|
||||||
|
notificationFrame.docShell.isAppTab = true;
|
||||||
|
if (notificationFrame.contentDocument.readyState == "complete" && wasAlive) {
|
||||||
|
dynamicResizer.start(panel, notificationFrame);
|
||||||
|
dispatchPanelEvent("socialFrameShow");
|
||||||
|
} else {
|
||||||
|
// first time load, wait for load and dispatch after load
|
||||||
|
notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
|
||||||
|
notificationFrame.removeEventListener("load", panelBrowserOnload, true);
|
||||||
|
dynamicResizer.start(panel, notificationFrame);
|
||||||
|
dispatchPanelEvent("socialFrameShow");
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let navBar = document.getElementById("nav-bar");
|
||||||
|
let anchor = navBar.getAttribute("mode") == "text" ?
|
||||||
|
document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-text") :
|
||||||
|
document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-badge-container");
|
||||||
|
// Bug 849216 - open the popup in a setTimeout so we avoid the auto-rollup
|
||||||
|
// handling from preventing it being opened in some cases.
|
||||||
|
setTimeout(function() {
|
||||||
|
panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
setPanelErrorMessage: function(aNotificationFrame) {
|
||||||
|
if (!aNotificationFrame)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let src = aNotificationFrame.getAttribute("src");
|
||||||
|
aNotificationFrame.removeAttribute("src");
|
||||||
|
aNotificationFrame.webNavigation.loadURI("about:socialerror?mode=tryAgainOnly&url=" +
|
||||||
|
encodeURIComponent(src),
|
||||||
|
null, null, null, null);
|
||||||
|
let panel = aNotificationFrame.parentNode;
|
||||||
|
sizeSocialPanelToContent(panel, aNotificationFrame);
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -23,6 +23,7 @@ MOCHITEST_BROWSER_FILES = \
|
|||||||
browser_social_multiprovider.js \
|
browser_social_multiprovider.js \
|
||||||
browser_social_multiworker.js \
|
browser_social_multiworker.js \
|
||||||
browser_social_errorPage.js \
|
browser_social_errorPage.js \
|
||||||
|
browser_social_status.js \
|
||||||
browser_social_window.js \
|
browser_social_window.js \
|
||||||
social_activate.html \
|
social_activate.html \
|
||||||
social_activate_iframe.html \
|
social_activate_iframe.html \
|
||||||
|
@ -50,9 +50,10 @@ function installListener(next, aManifest) {
|
|||||||
let expectEvent = "onInstalling";
|
let expectEvent = "onInstalling";
|
||||||
let prefname = getManifestPrefname(aManifest);
|
let prefname = getManifestPrefname(aManifest);
|
||||||
// wait for the actual removal to call next
|
// wait for the actual removal to call next
|
||||||
SocialService.registerProviderListener(function providerListener(topic, data) {
|
SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
|
||||||
if (topic == "provider-removed") {
|
if (topic == "provider-disabled") {
|
||||||
SocialService.unregisterProviderListener(providerListener);
|
SocialService.unregisterProviderListener(providerListener);
|
||||||
|
is(origin, aManifest.origin, "provider disabled");
|
||||||
executeSoon(next);
|
executeSoon(next);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -295,14 +296,15 @@ var tests = {
|
|||||||
Social.enabled = true;
|
Social.enabled = true;
|
||||||
|
|
||||||
// watch for the provider-update and test the new version
|
// watch for the provider-update and test the new version
|
||||||
SocialService.registerProviderListener(function providerListener(topic, data) {
|
SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
|
||||||
if (topic != "provider-update")
|
if (topic != "provider-update")
|
||||||
return;
|
return;
|
||||||
|
is(origin, addonManifest.origin, "provider updated")
|
||||||
SocialService.unregisterProviderListener(providerListener);
|
SocialService.unregisterProviderListener(providerListener);
|
||||||
Services.prefs.clearUserPref("social.whitelist");
|
Services.prefs.clearUserPref("social.whitelist");
|
||||||
let provider = Social._getProviderFromOrigin(addonManifest.origin);
|
let provider = Social._getProviderFromOrigin(origin);
|
||||||
is(provider.manifest.version, 2, "manifest version is 2");
|
is(provider.manifest.version, 2, "manifest version is 2");
|
||||||
Social.uninstallProvider(addonManifest.origin, function() {
|
Social.uninstallProvider(origin, function() {
|
||||||
gBrowser.removeTab(tab);
|
gBrowser.removeTab(tab);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
@ -155,14 +155,15 @@ var tests = {
|
|||||||
setManifestPref("social.manifest.blocked", manifest_bad);
|
setManifestPref("social.manifest.blocked", manifest_bad);
|
||||||
try {
|
try {
|
||||||
SocialService.addProvider(manifest_bad, function(provider) {
|
SocialService.addProvider(manifest_bad, function(provider) {
|
||||||
// the act of blocking should cause a 'provider-removed' notification
|
// the act of blocking should cause a 'provider-disabled' notification
|
||||||
// from SocialService.
|
// from SocialService.
|
||||||
SocialService.registerProviderListener(function providerListener(topic) {
|
SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
|
||||||
if (topic != "provider-removed")
|
if (topic != "provider-disabled")
|
||||||
return;
|
return;
|
||||||
SocialService.unregisterProviderListener(providerListener);
|
SocialService.unregisterProviderListener(providerListener);
|
||||||
|
is(origin, provider.origin, "provider disabled");
|
||||||
SocialService.getProvider(provider.origin, function(p) {
|
SocialService.getProvider(provider.origin, function(p) {
|
||||||
ok(p==null, "blocklisted provider removed");
|
ok(p == null, "blocklisted provider disabled");
|
||||||
Services.prefs.clearUserPref("social.manifest.blocked");
|
Services.prefs.clearUserPref("social.manifest.blocked");
|
||||||
resetBlocklist(finish);
|
resetBlocklist(finish);
|
||||||
});
|
});
|
||||||
|
@ -19,6 +19,13 @@ function test() {
|
|||||||
|
|
||||||
var tests = {
|
var tests = {
|
||||||
testStatusIcons: function(next) {
|
testStatusIcons: function(next) {
|
||||||
|
let icon = {
|
||||||
|
name: "testIcon",
|
||||||
|
iconURL: "chrome://browser/skin/Info.png",
|
||||||
|
contentPanel: "https://example.com/browser/browser/base/content/test/social/social_panel.html",
|
||||||
|
counter: 1
|
||||||
|
};
|
||||||
|
|
||||||
let iconsReady = false;
|
let iconsReady = false;
|
||||||
let gotSidebarMessage = false;
|
let gotSidebarMessage = false;
|
||||||
|
|
||||||
@ -71,7 +78,7 @@ var tests = {
|
|||||||
ok(true, "got sidebar message");
|
ok(true, "got sidebar message");
|
||||||
gotSidebarMessage = true;
|
gotSidebarMessage = true;
|
||||||
// load a status panel
|
// load a status panel
|
||||||
port.postMessage({topic: "test-ambient-notification"});
|
port.postMessage({topic: "test-ambient-notification", data: icon});
|
||||||
checkNext();
|
checkNext();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
220
browser/base/content/test/social/browser_social_status.js
Normal file
220
browser/base/content/test/social/browser_social_status.js
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
/* 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 SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
|
||||||
|
|
||||||
|
let manifest = { // builtin provider
|
||||||
|
name: "provider example.com",
|
||||||
|
origin: "https://example.com",
|
||||||
|
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||||
|
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||||
|
iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
|
||||||
|
};
|
||||||
|
let manifest2 = { // used for testing install
|
||||||
|
name: "provider test1",
|
||||||
|
origin: "https://test1.example.com",
|
||||||
|
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||||
|
statusURL: "https://test1.example.com/browser/browser/base/content/test/social/social_panel.html",
|
||||||
|
iconURL: "https://test1.example.com/browser/browser/base/content/test/moz.png",
|
||||||
|
version: 1
|
||||||
|
};
|
||||||
|
let manifest3 = { // used for testing install
|
||||||
|
name: "provider test2",
|
||||||
|
origin: "https://test2.example.com",
|
||||||
|
sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||||
|
iconURL: "https://test2.example.com/browser/browser/base/content/test/moz.png",
|
||||||
|
version: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function openWindowAndWaitForInit(callback) {
|
||||||
|
let topic = "browser-delayed-startup-finished";
|
||||||
|
let w = OpenBrowserWindow();
|
||||||
|
Services.obs.addObserver(function providerSet(subject, topic, data) {
|
||||||
|
Services.obs.removeObserver(providerSet, topic);
|
||||||
|
executeSoon(() => callback(w));
|
||||||
|
}, topic, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
|
||||||
|
let toolbar = document.getElementById("nav-bar");
|
||||||
|
let currentsetAtStart = toolbar.currentSet;
|
||||||
|
info("tb0 "+currentsetAtStart);
|
||||||
|
runSocialTestWithProvider(manifest, function () {
|
||||||
|
runSocialTests(tests, undefined, undefined, function () {
|
||||||
|
Services.prefs.clearUserPref("social.remote-install.enabled");
|
||||||
|
// just in case the tests failed, clear these here as well
|
||||||
|
Services.prefs.clearUserPref("social.allowMultipleWorkers");
|
||||||
|
Services.prefs.clearUserPref("social.whitelist");
|
||||||
|
|
||||||
|
// This post-test test ensures that a new window maintains the same
|
||||||
|
// toolbar button set as when we started. That means our insert/removal of
|
||||||
|
// persistent id's is working correctly
|
||||||
|
is(currentsetAtStart, toolbar.currentSet, "toolbar currentset unchanged");
|
||||||
|
openWindowAndWaitForInit(function(w1) {
|
||||||
|
checkSocialUI(w1);
|
||||||
|
// Sometimes the new window adds other buttons to currentSet that are
|
||||||
|
// outside the scope of what we're checking. So we verify that all
|
||||||
|
// buttons from startup are in currentSet for a new window, and that the
|
||||||
|
// provider buttons are properly removed. (e.g on try, window-controls
|
||||||
|
// was not present in currentsetAtStart, but present on the second
|
||||||
|
// window)
|
||||||
|
let tb1 = w1.document.getElementById("nav-bar");
|
||||||
|
info("tb0 "+toolbar.currentSet);
|
||||||
|
info("tb1 "+tb1.currentSet);
|
||||||
|
let startupSet = Set(toolbar.currentSet.split(','));
|
||||||
|
let newSet = Set(tb1.currentSet.split(','));
|
||||||
|
let intersect = Set([x for (x of startupSet) if (newSet.has(x))]);
|
||||||
|
info("intersect "+intersect);
|
||||||
|
let difference = Set([x for (x of newSet) if (!startupSet.has(x))]);
|
||||||
|
info("difference "+difference);
|
||||||
|
is(startupSet.size, intersect.size, "new window toolbar same as old");
|
||||||
|
// verify that our provider buttons are not in difference
|
||||||
|
let id = SocialStatus._toolbarHelper.idFromOrgin(manifest2.origin);
|
||||||
|
ok(!difference.has(id), "status button not persisted at end");
|
||||||
|
w1.close();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = {
|
||||||
|
testNoButtonOnInstall: function(next) {
|
||||||
|
// we expect the addon install dialog to appear, we need to accept the
|
||||||
|
// install from the dialog.
|
||||||
|
info("Waiting for install dialog");
|
||||||
|
let panel = document.getElementById("servicesInstall-notification");
|
||||||
|
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
|
||||||
|
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
|
||||||
|
info("servicesInstall-notification panel opened");
|
||||||
|
panel.button.click();
|
||||||
|
})
|
||||||
|
|
||||||
|
let id = "social-status-button-" + manifest3.origin;
|
||||||
|
let toolbar = document.getElementById("nav-bar");
|
||||||
|
let currentset = toolbar.getAttribute("currentset").split(',');
|
||||||
|
ok(currentset.indexOf(id) < 0, "button is not part of currentset at start");
|
||||||
|
|
||||||
|
let activationURL = manifest3.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||||
|
addTab(activationURL, function(tab) {
|
||||||
|
let doc = tab.linkedBrowser.contentDocument;
|
||||||
|
Social.installProvider(doc, manifest3, function(addonManifest) {
|
||||||
|
// enable the provider so we know the button would have appeared
|
||||||
|
SocialService.addBuiltinProvider(manifest3.origin, function(provider) {
|
||||||
|
ok(provider, "provider is installed");
|
||||||
|
currentset = toolbar.getAttribute("currentset").split(',');
|
||||||
|
ok(currentset.indexOf(id) < 0, "button was not added to currentset");
|
||||||
|
Social.uninstallProvider(manifest3.origin, function() {
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
testButtonOnInstall: function(next) {
|
||||||
|
// we expect the addon install dialog to appear, we need to accept the
|
||||||
|
// install from the dialog.
|
||||||
|
info("Waiting for install dialog");
|
||||||
|
let panel = document.getElementById("servicesInstall-notification");
|
||||||
|
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
|
||||||
|
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
|
||||||
|
info("servicesInstall-notification panel opened");
|
||||||
|
panel.button.click();
|
||||||
|
})
|
||||||
|
|
||||||
|
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||||
|
addTab(activationURL, function(tab) {
|
||||||
|
let doc = tab.linkedBrowser.contentDocument;
|
||||||
|
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||||
|
// at this point, we should have a button id in the currentset for our provider
|
||||||
|
let id = "social-status-button-" + manifest2.origin;
|
||||||
|
let toolbar = document.getElementById("nav-bar");
|
||||||
|
|
||||||
|
waitForCondition(function() {
|
||||||
|
let currentset = toolbar.getAttribute("currentset").split(',');
|
||||||
|
return currentset.indexOf(id) >= 0;
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
// no longer need the tab
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
next();
|
||||||
|
}, "status button added to currentset");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
testButtonOnEnable: function(next) {
|
||||||
|
// enable the provider now
|
||||||
|
SocialService.addBuiltinProvider(manifest2.origin, function(provider) {
|
||||||
|
ok(provider, "provider is installed");
|
||||||
|
let id = "social-status-button-" + manifest2.origin;
|
||||||
|
waitForCondition(function() { return document.getElementById(id) },
|
||||||
|
next, "button exists after enabling social");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
testStatusPanel: function(next) {
|
||||||
|
let icon = {
|
||||||
|
name: "testIcon",
|
||||||
|
iconURL: "chrome://browser/skin/Info.png",
|
||||||
|
counter: 1
|
||||||
|
};
|
||||||
|
// click on panel to open and wait for visibility
|
||||||
|
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
||||||
|
let id = "social-status-button-" + provider.origin;
|
||||||
|
let btn = document.getElementById(id)
|
||||||
|
ok(btn, "got a status button");
|
||||||
|
let port = provider.getWorkerPort();
|
||||||
|
|
||||||
|
port.onmessage = function (e) {
|
||||||
|
let topic = e.data.topic;
|
||||||
|
switch (topic) {
|
||||||
|
case "test-init-done":
|
||||||
|
ok(true, "test-init-done received");
|
||||||
|
ok(provider.profile.userName, "profile was set by test worker");
|
||||||
|
btn.click();
|
||||||
|
break;
|
||||||
|
case "got-social-panel-visibility":
|
||||||
|
ok(true, "got the panel message " + e.data.result);
|
||||||
|
if (e.data.result == "shown") {
|
||||||
|
let panel = document.getElementById("social-notification-panel");
|
||||||
|
panel.hidePopup();
|
||||||
|
} else {
|
||||||
|
port.postMessage({topic: "test-ambient-notification", data: icon});
|
||||||
|
port.close();
|
||||||
|
waitForCondition(function() { return btn.getAttribute("badge"); },
|
||||||
|
function() {
|
||||||
|
is(btn.style.listStyleImage, "url(\"" + icon.iconURL + "\")", "notification icon updated");
|
||||||
|
next();
|
||||||
|
}, "button updated by notification");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
port.postMessage({topic: "test-init"});
|
||||||
|
},
|
||||||
|
testButtonOnDisable: function(next) {
|
||||||
|
// enable the provider now
|
||||||
|
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
||||||
|
ok(provider, "provider is installed");
|
||||||
|
SocialService.removeProvider(manifest2.origin, function() {
|
||||||
|
let id = "social-status-button-" + manifest2.origin;
|
||||||
|
waitForCondition(function() { return !document.getElementById(id) },
|
||||||
|
next, "button does not exist after disabling the provider");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
testButtonOnUninstall: function(next) {
|
||||||
|
Social.uninstallProvider(manifest2.origin, function() {
|
||||||
|
// test that the button is no longer persisted
|
||||||
|
let id = "social-status-button-" + manifest2.origin;
|
||||||
|
let toolbar = document.getElementById("nav-bar");
|
||||||
|
let currentset = toolbar.getAttribute("currentset").split(',');
|
||||||
|
is(currentset.indexOf(id), -1, "button no longer in currentset");
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -177,7 +177,7 @@ function runSocialTests(tests, cbPreTest, cbPostTest, cbFinish) {
|
|||||||
cbPostTest(runNextTest);
|
cbPostTest(runNextTest);
|
||||||
}
|
}
|
||||||
cbPreTest(function() {
|
cbPreTest(function() {
|
||||||
is(providersAtStart, Social.providers.length, "pre-test: no new providers left enabled");
|
info("pre-test: starting with " + Social.providers.length + " providers");
|
||||||
info("sub-test " + name + " starting");
|
info("sub-test " + name + " starting");
|
||||||
try {
|
try {
|
||||||
func.call(tests, cleanupAndRunNextTest);
|
func.call(tests, cleanupAndRunNextTest);
|
||||||
|
@ -14,6 +14,7 @@ var data = {
|
|||||||
// at least one of these must be defined
|
// at least one of these must be defined
|
||||||
"sidebarURL": "/browser/browser/base/content/test/social/social_sidebar.html",
|
"sidebarURL": "/browser/browser/base/content/test/social/social_sidebar.html",
|
||||||
"workerURL": "/browser/browser/base/content/test/social/social_worker.js",
|
"workerURL": "/browser/browser/base/content/test/social/social_worker.js",
|
||||||
|
"statusURL": "/browser/browser/base/content/test/social/social_panel.html",
|
||||||
|
|
||||||
// should be available for display purposes
|
// should be available for display purposes
|
||||||
"description": "A short paragraph about this provider",
|
"description": "A short paragraph about this provider",
|
||||||
|
@ -119,13 +119,7 @@ onconnect = function(e) {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "test-ambient-notification":
|
case "test-ambient-notification":
|
||||||
let icon = {
|
apiPort.postMessage({topic: "social.ambient-notification", data: event.data.data});
|
||||||
name: "testIcon",
|
|
||||||
iconURL: "chrome://browser/skin/Info.png",
|
|
||||||
contentPanel: "https://example.com/browser/browser/base/content/test/social/social_panel.html",
|
|
||||||
counter: 1
|
|
||||||
};
|
|
||||||
apiPort.postMessage({topic: "social.ambient-notification", data: icon});
|
|
||||||
break;
|
break;
|
||||||
case "test-isVisible":
|
case "test-isVisible":
|
||||||
sidebarPort.postMessage({topic: "test-isVisible"});
|
sidebarPort.postMessage({topic: "test-isVisible"});
|
||||||
|
@ -20,6 +20,7 @@ let { Cc, Ci, Cu } = require("chrome");
|
|||||||
let promise = require("sdk/core/promise");
|
let promise = require("sdk/core/promise");
|
||||||
let Telemetry = require("devtools/shared/telemetry");
|
let Telemetry = require("devtools/shared/telemetry");
|
||||||
let TargetFactory = require("devtools/framework/target").TargetFactory;
|
let TargetFactory = require("devtools/framework/target").TargetFactory;
|
||||||
|
const escodegen = require("escodegen/escodegen");
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
@ -31,6 +32,8 @@ Cu.import("resource://gre/modules/jsdebugger.jsm");
|
|||||||
Cu.import("resource:///modules/devtools/gDevTools.jsm");
|
Cu.import("resource:///modules/devtools/gDevTools.jsm");
|
||||||
Cu.import("resource://gre/modules/osfile.jsm");
|
Cu.import("resource://gre/modules/osfile.jsm");
|
||||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||||
|
Cu.import("resource://gre/modules/reflect.jsm");
|
||||||
|
Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
|
XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
|
||||||
"resource:///modules/devtools/VariablesView.jsm");
|
"resource:///modules/devtools/VariablesView.jsm");
|
||||||
@ -521,6 +524,20 @@ var Scratchpad = {
|
|||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pretty print the source text inside the scratchpad.
|
||||||
|
*/
|
||||||
|
prettyPrint: function SP_prettyPrint() {
|
||||||
|
const uglyText = this.getText();
|
||||||
|
try {
|
||||||
|
const ast = Reflect.parse(uglyText);
|
||||||
|
const prettyText = escodegen.generate(ast);
|
||||||
|
this.setText(prettyText);
|
||||||
|
} catch (e) {
|
||||||
|
this.writeAsErrorComment(DevToolsUtils.safeErrorString(e));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes out a primitive value as a comment. This handles values which are
|
* Writes out a primitive value as a comment. This handles values which are
|
||||||
* to be printed directly (number, string) as well as grips to values
|
* to be printed directly (number, string) as well as grips to values
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
<command id="sp-cmd-run" oncommand="Scratchpad.run();"/>
|
<command id="sp-cmd-run" oncommand="Scratchpad.run();"/>
|
||||||
<command id="sp-cmd-inspect" oncommand="Scratchpad.inspect();"/>
|
<command id="sp-cmd-inspect" oncommand="Scratchpad.inspect();"/>
|
||||||
<command id="sp-cmd-display" oncommand="Scratchpad.display();"/>
|
<command id="sp-cmd-display" oncommand="Scratchpad.display();"/>
|
||||||
|
<command id="sp-cmd-pprint" oncommand="Scratchpad.prettyPrint();"/>
|
||||||
<command id="sp-cmd-contentContext" oncommand="Scratchpad.setContentContext();"/>
|
<command id="sp-cmd-contentContext" oncommand="Scratchpad.setContentContext();"/>
|
||||||
<command id="sp-cmd-browserContext" oncommand="Scratchpad.setBrowserContext();" disabled="true"/>
|
<command id="sp-cmd-browserContext" oncommand="Scratchpad.setBrowserContext();" disabled="true"/>
|
||||||
<command id="sp-cmd-reloadAndRun" oncommand="Scratchpad.reloadAndRun();"/>
|
<command id="sp-cmd-reloadAndRun" oncommand="Scratchpad.reloadAndRun();"/>
|
||||||
@ -94,6 +95,10 @@
|
|||||||
key="&display.key;"
|
key="&display.key;"
|
||||||
command="sp-cmd-display"
|
command="sp-cmd-display"
|
||||||
modifiers="accel"/>
|
modifiers="accel"/>
|
||||||
|
<key id="sp-key-pprint"
|
||||||
|
key="&pprint.key;"
|
||||||
|
command="sp-cmd-pprint"
|
||||||
|
modifiers="accel"/>
|
||||||
<key id="sp-key-reloadAndRun"
|
<key id="sp-key-reloadAndRun"
|
||||||
key="&reloadAndRun.key;"
|
key="&reloadAndRun.key;"
|
||||||
command="sp-cmd-reloadAndRun"
|
command="sp-cmd-reloadAndRun"
|
||||||
@ -272,6 +277,11 @@
|
|||||||
class="devtools-toolbarbutton"
|
class="devtools-toolbarbutton"
|
||||||
label="&display.label;"
|
label="&display.label;"
|
||||||
command="sp-cmd-display"/>
|
command="sp-cmd-display"/>
|
||||||
|
<toolbarspacer/>
|
||||||
|
<toolbarbutton id="sp-toolbar-pprint"
|
||||||
|
class="devtools-toolbarbutton"
|
||||||
|
label="&pprint.label;"
|
||||||
|
command="sp-cmd-pprint"/>
|
||||||
</toolbar>
|
</toolbar>
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ MOCHITEST_BROWSER_FILES = \
|
|||||||
browser_scratchpad_bug807924_cannot_convert_to_string.js \
|
browser_scratchpad_bug807924_cannot_convert_to_string.js \
|
||||||
browser_scratchpad_long_string.js \
|
browser_scratchpad_long_string.js \
|
||||||
browser_scratchpad_open_error_console.js \
|
browser_scratchpad_open_error_console.js \
|
||||||
|
browser_scratchpad_pprint.js \
|
||||||
head.js \
|
head.js \
|
||||||
|
|
||||||
# Disable test due to bug 807234 becoming basically permanent
|
# Disable test due to bug 807234 becoming basically permanent
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
gBrowser.selectedTab = gBrowser.addTab();
|
||||||
|
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||||
|
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||||
|
openScratchpad(runTests);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
content.location = "data:text/html;charset=utf8,test Scratchpad pretty print.";
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTests(sw)
|
||||||
|
{
|
||||||
|
const sp = sw.Scratchpad;
|
||||||
|
sp.setText("function main() { console.log(5); }");
|
||||||
|
sp.prettyPrint();
|
||||||
|
const prettyText = sp.getText();
|
||||||
|
ok(prettyText.contains("\n"));
|
||||||
|
finish();
|
||||||
|
}
|
@ -11,7 +11,7 @@ const promise = require("sdk/core/promise");
|
|||||||
|
|
||||||
let {CssLogic} = require("devtools/styleinspector/css-logic");
|
let {CssLogic} = require("devtools/styleinspector/css-logic");
|
||||||
let {InplaceEditor, editableField, editableItem} = require("devtools/shared/inplace-editor");
|
let {InplaceEditor, editableField, editableItem} = require("devtools/shared/inplace-editor");
|
||||||
let {ELEMENT_STYLE} = require("devtools/server/actors/styles");
|
let {ELEMENT_STYLE, PSEUDO_ELEMENTS} = require("devtools/server/actors/styles");
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
@ -203,7 +203,9 @@ ElementStyle.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mark overridden computed styles.
|
// Mark overridden computed styles.
|
||||||
this.markOverridden();
|
this.markOverriddenAll();
|
||||||
|
|
||||||
|
this._sortRulesForPseudoElement();
|
||||||
|
|
||||||
// We're done with the previous list of rules.
|
// We're done with the previous list of rules.
|
||||||
delete this._refreshRules;
|
delete this._refreshRules;
|
||||||
@ -215,6 +217,16 @@ ElementStyle.prototype = {
|
|||||||
return this.populated;
|
return this.populated;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put pseudo elements in front of others.
|
||||||
|
*/
|
||||||
|
_sortRulesForPseudoElement: function ElementStyle_sortRulesForPseudoElement()
|
||||||
|
{
|
||||||
|
this.rules = this.rules.sort((a, b) => {
|
||||||
|
return (a.pseudoElement || "z") > (b.pseudoElement || "z");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a rule if it's one we care about. Filters out duplicates and
|
* Add a rule if it's one we care about. Filters out duplicates and
|
||||||
* inherited styles with no inherited properties.
|
* inherited styles with no inherited properties.
|
||||||
@ -266,22 +278,38 @@ ElementStyle.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the properties listed in this.rules with an overridden flag
|
* Calls markOverridden with all supported pseudo elements
|
||||||
* if an earlier property overrides it.
|
|
||||||
*/
|
*/
|
||||||
markOverridden: function ElementStyle_markOverridden()
|
markOverriddenAll: function ElementStyle_markOverriddenAll()
|
||||||
|
{
|
||||||
|
this.markOverridden();
|
||||||
|
for (let pseudo of PSEUDO_ELEMENTS) {
|
||||||
|
this.markOverridden(pseudo);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark the properties listed in this.rules for a given pseudo element
|
||||||
|
* with an overridden flag if an earlier property overrides it.
|
||||||
|
* @param {string} pseudo
|
||||||
|
* Which pseudo element to flag as overridden.
|
||||||
|
* Empty string or undefined will default to no pseudo element.
|
||||||
|
*/
|
||||||
|
markOverridden: function ElementStyle_markOverridden(pseudo="")
|
||||||
{
|
{
|
||||||
// Gather all the text properties applied by these rules, ordered
|
// Gather all the text properties applied by these rules, ordered
|
||||||
// from more- to less-specific.
|
// from more- to less-specific.
|
||||||
let textProps = [];
|
let textProps = [];
|
||||||
for each (let rule in this.rules) {
|
for (let rule of this.rules) {
|
||||||
textProps = textProps.concat(rule.textProps.slice(0).reverse());
|
if (rule.pseudoElement == pseudo) {
|
||||||
|
textProps = textProps.concat(rule.textProps.slice(0).reverse());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather all the computed properties applied by those text
|
// Gather all the computed properties applied by those text
|
||||||
// properties.
|
// properties.
|
||||||
let computedProps = [];
|
let computedProps = [];
|
||||||
for each (let textProp in textProps) {
|
for (let textProp of textProps) {
|
||||||
computedProps = computedProps.concat(textProp.computed);
|
computedProps = computedProps.concat(textProp.computed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +330,7 @@ ElementStyle.prototype = {
|
|||||||
// _overriddenDirty will be set on each prop, indicating whether its
|
// _overriddenDirty will be set on each prop, indicating whether its
|
||||||
// dirty status changed during this pass.
|
// dirty status changed during this pass.
|
||||||
let taken = {};
|
let taken = {};
|
||||||
for each (let computedProp in computedProps) {
|
for (let computedProp of computedProps) {
|
||||||
let earlier = taken[computedProp.name];
|
let earlier = taken[computedProp.name];
|
||||||
let overridden;
|
let overridden;
|
||||||
if (earlier
|
if (earlier
|
||||||
@ -328,7 +356,7 @@ ElementStyle.prototype = {
|
|||||||
// computed properties are marked overridden. Update the text
|
// computed properties are marked overridden. Update the text
|
||||||
// property's associated editor, if any. This will clear the
|
// property's associated editor, if any. This will clear the
|
||||||
// _overriddenDirty state on all computed properties.
|
// _overriddenDirty state on all computed properties.
|
||||||
for each (let textProp in textProps) {
|
for (let textProp of textProps) {
|
||||||
// _updatePropertyOverridden will return true if the
|
// _updatePropertyOverridden will return true if the
|
||||||
// overridden state has changed for the text property.
|
// overridden state has changed for the text property.
|
||||||
if (this._updatePropertyOverridden(textProp)) {
|
if (this._updatePropertyOverridden(textProp)) {
|
||||||
@ -384,6 +412,7 @@ function Rule(aElementStyle, aOptions)
|
|||||||
this.domRule = aOptions.rule || null;
|
this.domRule = aOptions.rule || null;
|
||||||
this.style = aOptions.rule;
|
this.style = aOptions.rule;
|
||||||
this.matchedSelectors = aOptions.matchedSelectors || [];
|
this.matchedSelectors = aOptions.matchedSelectors || [];
|
||||||
|
this.pseudoElement = aOptions.pseudoElement || "";
|
||||||
|
|
||||||
this.inherited = aOptions.inherited || null;
|
this.inherited = aOptions.inherited || null;
|
||||||
this._modificationDepth = 0;
|
this._modificationDepth = 0;
|
||||||
@ -558,7 +587,7 @@ Rule.prototype = {
|
|||||||
textProp.priority = cssProp.priority;
|
textProp.priority = cssProp.priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.elementStyle.markOverridden();
|
this.elementStyle.markOverriddenAll();
|
||||||
|
|
||||||
if (promise === this._applyingModifications) {
|
if (promise === this._applyingModifications) {
|
||||||
this._applyingModifications = null;
|
this._applyingModifications = null;
|
||||||
@ -642,7 +671,6 @@ Rule.prototype = {
|
|||||||
let props = [];
|
let props = [];
|
||||||
|
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
dump("line: " + line + "\n");
|
|
||||||
let [, name, value, priority] = CSS_PROP_RE.exec(line) || []
|
let [, name, value, priority] = CSS_PROP_RE.exec(line) || []
|
||||||
if (!name || !value) {
|
if (!name || !value) {
|
||||||
continue;
|
continue;
|
||||||
@ -1078,6 +1106,7 @@ CssRuleView.prototype = {
|
|||||||
}
|
}
|
||||||
this._createEditors();
|
this._createEditors();
|
||||||
|
|
||||||
|
|
||||||
// Notify anyone that cares that we refreshed.
|
// Notify anyone that cares that we refreshed.
|
||||||
var evt = this.doc.createEvent("Events");
|
var evt = this.doc.createEvent("Events");
|
||||||
evt.initEvent("CssRuleViewRefreshed", true, false);
|
evt.initEvent("CssRuleViewRefreshed", true, false);
|
||||||
@ -1132,6 +1161,59 @@ CssRuleView.prototype = {
|
|||||||
this.element.dispatchEvent(evt);
|
this.element.dispatchEvent(evt);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text for header that shows above rules for this element
|
||||||
|
*/
|
||||||
|
get selectedElementLabel ()
|
||||||
|
{
|
||||||
|
if (this._selectedElementLabel) {
|
||||||
|
return this._selectedElementLabel;
|
||||||
|
}
|
||||||
|
this._selectedElementLabel = CssLogic.l10n("rule.selectedElement");
|
||||||
|
return this._selectedElementLabel;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text for header that shows above rules for pseudo elements
|
||||||
|
*/
|
||||||
|
get pseudoElementLabel ()
|
||||||
|
{
|
||||||
|
if (this._pseudoElementLabel) {
|
||||||
|
return this._pseudoElementLabel;
|
||||||
|
}
|
||||||
|
this._pseudoElementLabel = CssLogic.l10n("rule.pseudoElement");
|
||||||
|
return this._pseudoElementLabel;
|
||||||
|
},
|
||||||
|
|
||||||
|
togglePseudoElementVisibility: function(value)
|
||||||
|
{
|
||||||
|
this._showPseudoElements = !!value;
|
||||||
|
let isOpen = this.showPseudoElements;
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("devtools.inspector.show_pseudo_elements",
|
||||||
|
isOpen);
|
||||||
|
|
||||||
|
this.element.classList.toggle("show-pseudo-elements", isOpen);
|
||||||
|
|
||||||
|
if (this.pseudoElementTwisty) {
|
||||||
|
if (isOpen) {
|
||||||
|
this.pseudoElementTwisty.setAttribute("open", "true");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.pseudoElementTwisty.removeAttribute("open");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get showPseudoElements ()
|
||||||
|
{
|
||||||
|
if (this._showPseudoElements === undefined) {
|
||||||
|
this._showPseudoElements =
|
||||||
|
Services.prefs.getBoolPref("devtools.inspector.show_pseudo_elements");
|
||||||
|
}
|
||||||
|
return this._showPseudoElements;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates editor UI for each of the rules in _elementStyle.
|
* Creates editor UI for each of the rules in _elementStyle.
|
||||||
*/
|
*/
|
||||||
@ -1140,18 +1222,48 @@ CssRuleView.prototype = {
|
|||||||
// Run through the current list of rules, attaching
|
// Run through the current list of rules, attaching
|
||||||
// their editors in order. Create editors if needed.
|
// their editors in order. Create editors if needed.
|
||||||
let lastInheritedSource = "";
|
let lastInheritedSource = "";
|
||||||
|
let seenPseudoElement = false;
|
||||||
|
let seenNormalElement = false;
|
||||||
|
|
||||||
for (let rule of this._elementStyle.rules) {
|
for (let rule of this._elementStyle.rules) {
|
||||||
if (rule.domRule.system) {
|
if (rule.domRule.system) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only print header for this element if there are pseudo elements
|
||||||
|
if (seenPseudoElement && !seenNormalElement && !rule.pseudoElement) {
|
||||||
|
seenNormalElement = true;
|
||||||
|
let div = this.doc.createElementNS(HTML_NS, "div");
|
||||||
|
div.className = "theme-gutter ruleview-header";
|
||||||
|
div.textContent = this.selectedElementLabel;
|
||||||
|
this.element.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
let inheritedSource = rule.inheritedSource;
|
let inheritedSource = rule.inheritedSource;
|
||||||
if (inheritedSource != lastInheritedSource) {
|
if (inheritedSource != lastInheritedSource) {
|
||||||
let h2 = this.doc.createElementNS(HTML_NS, "div");
|
let div = this.doc.createElementNS(HTML_NS, "div");
|
||||||
h2.className = "ruleview-rule-inheritance theme-gutter";
|
div.className = "theme-gutter ruleview-header";
|
||||||
h2.textContent = inheritedSource;
|
div.textContent = inheritedSource;
|
||||||
lastInheritedSource = inheritedSource;
|
lastInheritedSource = inheritedSource;
|
||||||
this.element.appendChild(h2);
|
this.element.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!seenPseudoElement && rule.pseudoElement) {
|
||||||
|
seenPseudoElement = true;
|
||||||
|
|
||||||
|
let div = this.doc.createElementNS(HTML_NS, "div");
|
||||||
|
div.className = "theme-gutter ruleview-header";
|
||||||
|
div.textContent = this.pseudoElementLabel;
|
||||||
|
|
||||||
|
let twisty = this.pseudoElementTwisty =
|
||||||
|
this.doc.createElementNS(HTML_NS, "span");
|
||||||
|
twisty.className = "ruleview-expander theme-twisty";
|
||||||
|
twisty.addEventListener("click", () => {
|
||||||
|
this.togglePseudoElementVisibility(!this.showPseudoElements);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
div.insertBefore(twisty, div.firstChild);
|
||||||
|
this.element.appendChild(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rule.editor) {
|
if (!rule.editor) {
|
||||||
@ -1160,6 +1272,8 @@ CssRuleView.prototype = {
|
|||||||
|
|
||||||
this.element.appendChild(rule.editor.element);
|
this.element.appendChild(rule.editor.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.togglePseudoElementVisibility(this.showPseudoElements);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1227,6 +1341,9 @@ RuleEditor.prototype = {
|
|||||||
this.element = this.doc.createElementNS(HTML_NS, "div");
|
this.element = this.doc.createElementNS(HTML_NS, "div");
|
||||||
this.element.className = "ruleview-rule theme-separator";
|
this.element.className = "ruleview-rule theme-separator";
|
||||||
this.element._ruleEditor = this;
|
this.element._ruleEditor = this;
|
||||||
|
if (this.rule.pseudoElement) {
|
||||||
|
this.element.classList.add("ruleview-rule-pseudo-element");
|
||||||
|
}
|
||||||
|
|
||||||
// Give a relative position for the inplace editor's measurement
|
// Give a relative position for the inplace editor's measurement
|
||||||
// span to be placed absolutely against.
|
// span to be placed absolutely against.
|
||||||
@ -1358,12 +1475,15 @@ RuleEditor.prototype = {
|
|||||||
* Property value.
|
* Property value.
|
||||||
* @param {string} aPriority
|
* @param {string} aPriority
|
||||||
* Property priority.
|
* Property priority.
|
||||||
|
* @return {TextProperty}
|
||||||
|
* The new property
|
||||||
*/
|
*/
|
||||||
addProperty: function RuleEditor_addProperty(aName, aValue, aPriority)
|
addProperty: function RuleEditor_addProperty(aName, aValue, aPriority)
|
||||||
{
|
{
|
||||||
let prop = this.rule.createProperty(aName, aValue, aPriority);
|
let prop = this.rule.createProperty(aName, aValue, aPriority);
|
||||||
let editor = new TextPropertyEditor(this, prop);
|
let editor = new TextPropertyEditor(this, prop);
|
||||||
this.propertyList.appendChild(editor.element);
|
this.propertyList.appendChild(editor.element);
|
||||||
|
return prop;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,3 +36,21 @@
|
|||||||
.ruleview-warning[hidden] {
|
.ruleview-warning[hidden] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ruleview-rule-pseudo-element {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-pseudo-elements .ruleview-rule-pseudo-element {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruleview .ruleview-expander {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruleview-header {
|
||||||
|
vertical-align:middle;
|
||||||
|
height: 1.5em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
@ -35,6 +35,7 @@ MOCHITEST_BROWSER_FILES = \
|
|||||||
browser_bug894376_css_value_completion_new_property_value_pair.js \
|
browser_bug894376_css_value_completion_new_property_value_pair.js \
|
||||||
browser_bug894376_css_value_completion_existing_property_value_pair.js \
|
browser_bug894376_css_value_completion_existing_property_value_pair.js \
|
||||||
browser_ruleview_bug_902966_revert_value_on_ESC.js \
|
browser_ruleview_bug_902966_revert_value_on_ESC.js \
|
||||||
|
browser_ruleview_pseudoelement.js \
|
||||||
head.js \
|
head.js \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
@ -52,4 +53,5 @@ MOCHITEST_BROWSER_FILES += \
|
|||||||
browser_styleinspector_bug_677930_urls_clickable \
|
browser_styleinspector_bug_677930_urls_clickable \
|
||||||
browser_styleinspector_bug_677930_urls_clickable/browser_styleinspector_bug_677930_urls_clickable.css \
|
browser_styleinspector_bug_677930_urls_clickable/browser_styleinspector_bug_677930_urls_clickable.css \
|
||||||
test-image.png \
|
test-image.png \
|
||||||
|
browser_ruleview_pseudoelement.html \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
@ -0,0 +1,115 @@
|
|||||||
|
<!-- Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
float:left;
|
||||||
|
width: 128px;
|
||||||
|
height: 128px;
|
||||||
|
background: #ddd;
|
||||||
|
padding: 32px;
|
||||||
|
margin: 32px;
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
nothing {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
p::-moz-selection {
|
||||||
|
color: white;
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
p::selection {
|
||||||
|
color: white;
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
p:first-line {
|
||||||
|
background: blue;
|
||||||
|
}
|
||||||
|
p:first-letter {
|
||||||
|
color: red;
|
||||||
|
font-size: 130%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box:before {
|
||||||
|
background: green;
|
||||||
|
content: " ";
|
||||||
|
position: absolute;
|
||||||
|
height:32px;
|
||||||
|
width:32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box:after {
|
||||||
|
background: red;
|
||||||
|
content: " ";
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
height:32px;
|
||||||
|
width:32px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: -16px;
|
||||||
|
margin-left: -16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topleft:before {
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topright:before {
|
||||||
|
top:0;
|
||||||
|
right:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottomright:before {
|
||||||
|
bottom:10px;
|
||||||
|
right:10px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottomright:before {
|
||||||
|
bottom:0;
|
||||||
|
right:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottomleft:before {
|
||||||
|
bottom:0;
|
||||||
|
left:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>ruleview pseudoelement($("test"));</h1>
|
||||||
|
|
||||||
|
<div id="topleft" class="box topleft">
|
||||||
|
<p>Top Left<br />Position</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="topright" class="box topright">
|
||||||
|
<p>Top Right<br />Position</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="bottomright" class="box bottomright">
|
||||||
|
<p>Bottom Right<br />Position</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="bottomleft" class="box bottomleft">
|
||||||
|
<p>Bottom Left<br />Position</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,317 @@
|
|||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
let doc;
|
||||||
|
let inspector;
|
||||||
|
let view;
|
||||||
|
|
||||||
|
const TEST_URI = "http://example.com/browser/browser/" +
|
||||||
|
"devtools/styleinspector/test/" +
|
||||||
|
"browser_ruleview_pseudoelement.html";
|
||||||
|
|
||||||
|
function testPseudoElements(aInspector, aRuleView)
|
||||||
|
{
|
||||||
|
inspector = aInspector;
|
||||||
|
view = aRuleView;
|
||||||
|
|
||||||
|
testTopLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTopLeft()
|
||||||
|
{
|
||||||
|
testNode(doc.querySelector("#topleft"), (element, elementStyle) => {
|
||||||
|
let elementRules = elementStyle.rules.filter((rule) => { return !rule.pseudoElement; });
|
||||||
|
let afterRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":after"; });
|
||||||
|
let beforeRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":before"; });
|
||||||
|
let firstLineRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":first-line"; });
|
||||||
|
let firstLetterRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":first-letter"; });
|
||||||
|
let selectionRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":-moz-selection"; });
|
||||||
|
|
||||||
|
is(elementRules.length, 4, "TopLeft has the correct number of non psuedo element rules");
|
||||||
|
is(afterRules.length, 1, "TopLeft has the correct number of :after rules");
|
||||||
|
is(beforeRules.length, 2, "TopLeft has the correct number of :before rules");
|
||||||
|
is(firstLineRules.length, 0, "TopLeft has the correct number of :first-line rules");
|
||||||
|
is(firstLetterRules.length, 0, "TopLeft has the correct number of :first-letter rules");
|
||||||
|
is(selectionRules.length, 0, "TopLeft has the correct number of :selection rules");
|
||||||
|
|
||||||
|
let gutters = view.element.querySelectorAll(".theme-gutter");
|
||||||
|
is (gutters.length, 3, "There are three gutter headings");
|
||||||
|
is (gutters[0].textContent, "Pseudo-elements", "Gutter heading is correct");
|
||||||
|
is (gutters[1].textContent, "This Element", "Gutter heading is correct");
|
||||||
|
is (gutters[2].textContent, "Inherited from body", "Gutter heading is correct");
|
||||||
|
|
||||||
|
// Make sure that clicking on the twisty hides pseudo elements
|
||||||
|
let expander = gutters[0].querySelector(".ruleview-expander");
|
||||||
|
ok (view.element.classList.contains("show-pseudo-elements"), "Pseudo Elements are expanded");
|
||||||
|
expander.click();
|
||||||
|
ok (!view.element.classList.contains("show-pseudo-elements"), "Pseudo Elements are collapsed by twisty");
|
||||||
|
expander.click();
|
||||||
|
ok (view.element.classList.contains("show-pseudo-elements"), "Pseudo Elements are expanded again");
|
||||||
|
expander.click();
|
||||||
|
|
||||||
|
let defaultView = element.ownerDocument.defaultView;
|
||||||
|
let elementRule = elementRules[0];
|
||||||
|
let elementRuleView = [].filter.call(view.element.children, (e) => {
|
||||||
|
return e._ruleEditor && e._ruleEditor.rule === elementRule;
|
||||||
|
})[0]._ruleEditor;
|
||||||
|
|
||||||
|
let elementAfterRule = afterRules[0];
|
||||||
|
let elementAfterRuleView = [].filter.call(view.element.children, (e) => {
|
||||||
|
return e._ruleEditor && e._ruleEditor.rule === elementAfterRule;
|
||||||
|
})[0]._ruleEditor;
|
||||||
|
|
||||||
|
is
|
||||||
|
(
|
||||||
|
convertTextPropsToString(elementAfterRule.textProps),
|
||||||
|
"background: none repeat scroll 0% 0% red; content: \" \"; position: absolute; " +
|
||||||
|
"border-radius: 50%; height: 32px; width: 32px; top: 50%; left: 50%; margin-top: -16px; margin-left: -16px",
|
||||||
|
"TopLeft after properties are correct"
|
||||||
|
);
|
||||||
|
|
||||||
|
let elementBeforeRule = beforeRules[0];
|
||||||
|
let elementBeforeRuleView = [].filter.call(view.element.children, (e) => {
|
||||||
|
return e._ruleEditor && e._ruleEditor.rule === elementBeforeRule;
|
||||||
|
})[0]._ruleEditor;
|
||||||
|
|
||||||
|
is
|
||||||
|
(
|
||||||
|
convertTextPropsToString(elementBeforeRule.textProps),
|
||||||
|
"top: 0px; left: 0px",
|
||||||
|
"TopLeft before properties are correct"
|
||||||
|
);
|
||||||
|
|
||||||
|
let firstProp = elementAfterRuleView.addProperty("background-color", "rgb(0, 255, 0)", "");
|
||||||
|
let secondProp = elementAfterRuleView.addProperty("padding", "100px", "");
|
||||||
|
|
||||||
|
is (firstProp, elementAfterRule.textProps[elementAfterRule.textProps.length - 2],
|
||||||
|
"First added property is on back of array");
|
||||||
|
is (secondProp, elementAfterRule.textProps[elementAfterRule.textProps.length - 1],
|
||||||
|
"Second added property is on back of array");
|
||||||
|
|
||||||
|
promiseDone(elementAfterRule._applyingModifications.then(() => {
|
||||||
|
is(defaultView.getComputedStyle(element, ":after").getPropertyValue("background-color"),
|
||||||
|
"rgb(0, 255, 0)", "Added property should have been used.");
|
||||||
|
is(defaultView.getComputedStyle(element, ":after").getPropertyValue("padding-top"),
|
||||||
|
"100px", "Added property should have been used.");
|
||||||
|
is(defaultView.getComputedStyle(element).getPropertyValue("padding-top"),
|
||||||
|
"32px", "Added property should not apply to element");
|
||||||
|
|
||||||
|
secondProp.setEnabled(false);
|
||||||
|
|
||||||
|
return elementAfterRule._applyingModifications;
|
||||||
|
}).then(() => {
|
||||||
|
is(defaultView.getComputedStyle(element, ":after").getPropertyValue("padding-top"), "0px",
|
||||||
|
"Disabled property should have been used.");
|
||||||
|
is(defaultView.getComputedStyle(element).getPropertyValue("padding-top"), "32px",
|
||||||
|
"Added property should not apply to element");
|
||||||
|
|
||||||
|
secondProp.setEnabled(true);
|
||||||
|
|
||||||
|
return elementAfterRule._applyingModifications;
|
||||||
|
}).then(() => {
|
||||||
|
is(defaultView.getComputedStyle(element, ":after").getPropertyValue("padding-top"), "100px",
|
||||||
|
"Enabled property should have been used.");
|
||||||
|
is(defaultView.getComputedStyle(element).getPropertyValue("padding-top"), "32px",
|
||||||
|
"Added property should not apply to element");
|
||||||
|
|
||||||
|
let firstProp = elementRuleView.addProperty("background-color", "rgb(0, 0, 255)", "");
|
||||||
|
|
||||||
|
return elementRule._applyingModifications;
|
||||||
|
}).then(() => {
|
||||||
|
is(defaultView.getComputedStyle(element).getPropertyValue("background-color"), "rgb(0, 0, 255)",
|
||||||
|
"Added property should have been used.");
|
||||||
|
is(defaultView.getComputedStyle(element, ":after").getPropertyValue("background-color"), "rgb(0, 255, 0)",
|
||||||
|
"Added prop does not apply to pseudo");
|
||||||
|
|
||||||
|
testTopRight();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTopRight()
|
||||||
|
{
|
||||||
|
testNode(doc.querySelector("#topright"), (element, elementStyle) => {
|
||||||
|
|
||||||
|
let elementRules = elementStyle.rules.filter((rule) => { return !rule.pseudoElement; });
|
||||||
|
let afterRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":after"; });
|
||||||
|
let beforeRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":before"; });
|
||||||
|
let firstLineRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":first-line"; });
|
||||||
|
let firstLetterRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":first-letter"; });
|
||||||
|
let selectionRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":-moz-selection"; });
|
||||||
|
|
||||||
|
is(elementRules.length, 4, "TopRight has the correct number of non psuedo element rules");
|
||||||
|
is(afterRules.length, 1, "TopRight has the correct number of :after rules");
|
||||||
|
is(beforeRules.length, 2, "TopRight has the correct number of :before rules");
|
||||||
|
is(firstLineRules.length, 0, "TopRight has the correct number of :first-line rules");
|
||||||
|
is(firstLetterRules.length, 0, "TopRight has the correct number of :first-letter rules");
|
||||||
|
is(selectionRules.length, 0, "TopRight has the correct number of :selection rules");
|
||||||
|
|
||||||
|
let gutters = view.element.querySelectorAll(".theme-gutter");
|
||||||
|
is (gutters.length, 3, "There are three gutter headings");
|
||||||
|
is (gutters[0].textContent, "Pseudo-elements", "Gutter heading is correct");
|
||||||
|
is (gutters[1].textContent, "This Element", "Gutter heading is correct");
|
||||||
|
is (gutters[2].textContent, "Inherited from body", "Gutter heading is correct");
|
||||||
|
|
||||||
|
let expander = gutters[0].querySelector(".ruleview-expander");
|
||||||
|
ok (!view.element.classList.contains("show-pseudo-elements"), "Pseudo Elements remain collapsed after switching element");
|
||||||
|
expander.scrollIntoView();
|
||||||
|
expander.click();
|
||||||
|
ok (view.element.classList.contains("show-pseudo-elements"), "Pseudo Elements are shown again after clicking twisty");
|
||||||
|
|
||||||
|
testBottomRight();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBottomRight()
|
||||||
|
{
|
||||||
|
testNode(doc.querySelector("#bottomright"), (element, elementStyle) => {
|
||||||
|
|
||||||
|
let elementRules = elementStyle.rules.filter((rule) => { return !rule.pseudoElement; });
|
||||||
|
let afterRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":after"; });
|
||||||
|
let beforeRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":before"; });
|
||||||
|
let firstLineRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":first-line"; });
|
||||||
|
let firstLetterRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":first-letter"; });
|
||||||
|
let selectionRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":-moz-selection"; });
|
||||||
|
|
||||||
|
is(elementRules.length, 4, "BottomRight has the correct number of non psuedo element rules");
|
||||||
|
is(afterRules.length, 1, "BottomRight has the correct number of :after rules");
|
||||||
|
is(beforeRules.length, 3, "BottomRight has the correct number of :before rules");
|
||||||
|
is(firstLineRules.length, 0, "BottomRight has the correct number of :first-line rules");
|
||||||
|
is(firstLetterRules.length, 0, "BottomRight has the correct number of :first-letter rules");
|
||||||
|
is(selectionRules.length, 0, "BottomRight has the correct number of :selection rules");
|
||||||
|
|
||||||
|
testBottomLeft();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBottomLeft()
|
||||||
|
{
|
||||||
|
testNode(doc.querySelector("#bottomleft"), (element, elementStyle) => {
|
||||||
|
|
||||||
|
let elementRules = elementStyle.rules.filter((rule) => { return !rule.pseudoElement; });
|
||||||
|
let afterRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":after"; });
|
||||||
|
let beforeRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":before"; });
|
||||||
|
let firstLineRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":first-line"; });
|
||||||
|
let firstLetterRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":first-letter"; });
|
||||||
|
let selectionRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":-moz-selection"; });
|
||||||
|
|
||||||
|
is(elementRules.length, 4, "BottomLeft has the correct number of non psuedo element rules");
|
||||||
|
is(afterRules.length, 1, "BottomLeft has the correct number of :after rules");
|
||||||
|
is(beforeRules.length, 2, "BottomLeft has the correct number of :before rules");
|
||||||
|
is(firstLineRules.length, 0, "BottomLeft has the correct number of :first-line rules");
|
||||||
|
is(firstLetterRules.length, 0, "BottomLeft has the correct number of :first-letter rules");
|
||||||
|
is(selectionRules.length, 0, "BottomLeft has the correct number of :selection rules");
|
||||||
|
|
||||||
|
testParagraph();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testParagraph()
|
||||||
|
{
|
||||||
|
testNode(doc.querySelector("#bottomleft p"), (element, elementStyle) => {
|
||||||
|
|
||||||
|
let elementRules = elementStyle.rules.filter((rule) => { return !rule.pseudoElement; });
|
||||||
|
let afterRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":after"; });
|
||||||
|
let beforeRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":before"; });
|
||||||
|
let firstLineRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":first-line"; });
|
||||||
|
let firstLetterRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":first-letter"; });
|
||||||
|
let selectionRules = elementStyle.rules.filter((rule) => { return rule.pseudoElement === ":-moz-selection"; });
|
||||||
|
|
||||||
|
is(elementRules.length, 3, "Paragraph has the correct number of non psuedo element rules");
|
||||||
|
is(afterRules.length, 0, "Paragraph has the correct number of :after rules");
|
||||||
|
is(beforeRules.length, 0, "Paragraph has the correct number of :before rules");
|
||||||
|
is(firstLineRules.length, 1, "Paragraph has the correct number of :first-line rules");
|
||||||
|
is(firstLetterRules.length, 1, "Paragraph has the correct number of :first-letter rules");
|
||||||
|
is(selectionRules.length, 1, "Paragraph has the correct number of :selection rules");
|
||||||
|
|
||||||
|
let gutters = view.element.querySelectorAll(".theme-gutter");
|
||||||
|
is (gutters.length, 3, "There are three gutter headings");
|
||||||
|
is (gutters[0].textContent, "Pseudo-elements", "Gutter heading is correct");
|
||||||
|
is (gutters[1].textContent, "This Element", "Gutter heading is correct");
|
||||||
|
is (gutters[2].textContent, "Inherited from body", "Gutter heading is correct");
|
||||||
|
|
||||||
|
let elementFirstLineRule = firstLineRules[0];
|
||||||
|
let elementFirstLineRuleView = [].filter.call(view.element.children, (e) => {
|
||||||
|
return e._ruleEditor && e._ruleEditor.rule === elementFirstLineRule;
|
||||||
|
})[0]._ruleEditor;
|
||||||
|
|
||||||
|
is
|
||||||
|
(
|
||||||
|
convertTextPropsToString(elementFirstLineRule.textProps),
|
||||||
|
"background: none repeat scroll 0% 0% blue",
|
||||||
|
"Paragraph first-line properties are correct"
|
||||||
|
);
|
||||||
|
|
||||||
|
let elementFirstLetterRule = firstLetterRules[0];
|
||||||
|
let elementFirstLetterRuleView = [].filter.call(view.element.children, (e) => {
|
||||||
|
return e._ruleEditor && e._ruleEditor.rule === elementFirstLetterRule;
|
||||||
|
})[0]._ruleEditor;
|
||||||
|
|
||||||
|
is
|
||||||
|
(
|
||||||
|
convertTextPropsToString(elementFirstLetterRule.textProps),
|
||||||
|
"color: red; font-size: 130%",
|
||||||
|
"Paragraph first-letter properties are correct"
|
||||||
|
);
|
||||||
|
|
||||||
|
let elementSelectionRule = selectionRules[0];
|
||||||
|
let elementSelectionRuleView = [].filter.call(view.element.children, (e) => {
|
||||||
|
return e._ruleEditor && e._ruleEditor.rule === elementSelectionRule;
|
||||||
|
})[0]._ruleEditor;
|
||||||
|
|
||||||
|
is
|
||||||
|
(
|
||||||
|
convertTextPropsToString(elementSelectionRule.textProps),
|
||||||
|
"color: white; background: none repeat scroll 0% 0% black",
|
||||||
|
"Paragraph first-letter properties are correct"
|
||||||
|
);
|
||||||
|
|
||||||
|
testBody();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBody() {
|
||||||
|
|
||||||
|
testNode(doc.querySelector("body"), (element, elementStyle) => {
|
||||||
|
|
||||||
|
let gutters = view.element.querySelectorAll(".theme-gutter");
|
||||||
|
is (gutters.length, 0, "There are no gutter headings");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
function convertTextPropsToString(textProps) {
|
||||||
|
return textProps.map((t) => {
|
||||||
|
return t.name + ": " + t.value;
|
||||||
|
}).join("; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function testNode(node, cb)
|
||||||
|
{
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
cb(node, view._elementStyle)
|
||||||
|
});
|
||||||
|
inspector.selection.setNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishTest()
|
||||||
|
{
|
||||||
|
doc = null;
|
||||||
|
gBrowser.removeCurrentTab();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
waitForExplicitFinish();
|
||||||
|
gBrowser.selectedTab = gBrowser.addTab();
|
||||||
|
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||||
|
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||||
|
doc = content.document;
|
||||||
|
waitForFocus(() => openRuleView(testPseudoElements), content);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
content.location = TEST_URI;
|
||||||
|
}
|
@ -65,6 +65,9 @@
|
|||||||
<!ENTITY display.accesskey "D">
|
<!ENTITY display.accesskey "D">
|
||||||
<!ENTITY display.key "l">
|
<!ENTITY display.key "l">
|
||||||
|
|
||||||
|
<!ENTITY pprint.label "Pretty Print">
|
||||||
|
<!ENTITY pprint.key "p">
|
||||||
|
|
||||||
<!-- LOCALIZATION NOTE (environmentMenu.label, accesskey): This menu item was
|
<!-- LOCALIZATION NOTE (environmentMenu.label, accesskey): This menu item was
|
||||||
- renamed from "Context" to avoid confusion with the right-click context
|
- renamed from "Context" to avoid confusion with the right-click context
|
||||||
- menu in the text area. It refers to the JavaScript Environment (or context)
|
- menu in the text area. It refers to the JavaScript Environment (or context)
|
||||||
|
@ -35,6 +35,14 @@ rule.sourceElement=element
|
|||||||
# e.g "Inherited from body#bodyID"
|
# e.g "Inherited from body#bodyID"
|
||||||
rule.inheritedFrom=Inherited from %S
|
rule.inheritedFrom=Inherited from %S
|
||||||
|
|
||||||
|
# LOCALIZATION NOTE (rule.pseudoElement): Shown for CSS rules
|
||||||
|
# pseudo element header
|
||||||
|
rule.pseudoElement=Pseudo-elements
|
||||||
|
|
||||||
|
# LOCALIZATION NOTE (rule.pseudoElement): Shown for CSS rules
|
||||||
|
# pseudo element header
|
||||||
|
rule.selectedElement=This Element
|
||||||
|
|
||||||
# LOCALIZATION NOTE (helpLinkTitle): For each style property
|
# LOCALIZATION NOTE (helpLinkTitle): For each style property
|
||||||
# the user can hover it and get a help link button which allows one to
|
# the user can hover it and get a help link button which allows one to
|
||||||
# quickly jump to the documentation from the Mozilla Developer Network site.
|
# quickly jump to the documentation from the Mozilla Developer Network site.
|
||||||
|
@ -42,32 +42,28 @@ function isSelectable(aElement) {
|
|||||||
// placeholder logic
|
// placeholder logic
|
||||||
return aElement.nodeName == 'richgriditem';
|
return aElement.nodeName == 'richgriditem';
|
||||||
}
|
}
|
||||||
|
|
||||||
function withinCone(aLen, aHeight) {
|
function withinCone(aLen, aHeight) {
|
||||||
// check pt falls within 45deg either side of the cross axis
|
// check pt falls within 45deg either side of the cross axis
|
||||||
return aLen > aHeight;
|
return aLen > aHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getScrollAxisFromElement(aElement) {
|
function getScrollAxisFromElement(aElement) {
|
||||||
let elem = aElement,
|
// keeping it simple - just return apparent scroll axis for the document
|
||||||
win = elem.ownerDocument.defaultView;
|
let win = aElement.ownerDocument.defaultView;
|
||||||
let scrollX, scrollY;
|
let scrollX = win.scrollMaxX,
|
||||||
for (; elem && 1==elem.nodeType; elem = elem.parentNode) {
|
scrollY = win.scrollMaxY;
|
||||||
let cs = win.getComputedStyle(elem);
|
// determine scroll axis from scrollable content when possible
|
||||||
scrollX = (cs.overflowX=='scroll' || cs.overflowX=='auto');
|
if (scrollX || scrollY)
|
||||||
scrollY = (cs.overflowX=='scroll' || cs.overflowX=='auto');
|
return scrollX >= scrollY ? 'x' : 'y';
|
||||||
if (scrollX || scrollY) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return scrollX ? 'x' : 'y';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// fall back to guessing at scroll axis from document aspect ratio
|
||||||
|
let docElem = aElement.ownerDocument.documentElement;
|
||||||
|
return docElem.clientWidth >= docElem.clientHeight ?
|
||||||
|
'x' : 'y';
|
||||||
|
}
|
||||||
function pointFromTouchEvent(aEvent) {
|
function pointFromTouchEvent(aEvent) {
|
||||||
let touch = aEvent.touches[0];
|
let touch = aEvent.touches[0];
|
||||||
return { x: touch.clientX, y: touch.clientY };
|
return { x: touch.clientX, y: touch.clientY };
|
||||||
}
|
}
|
||||||
|
|
||||||
// This damping function has these important properties:
|
// This damping function has these important properties:
|
||||||
// f(0) = 0
|
// f(0) = 0
|
||||||
// f'(0) = 1
|
// f'(0) = 1
|
||||||
|
@ -169,24 +169,28 @@ this.Social = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register an observer for changes to the provider list
|
// Register an observer for changes to the provider list
|
||||||
SocialService.registerProviderListener(function providerListener(topic, data) {
|
SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
|
||||||
// An engine change caused by adding/removing a provider should notify.
|
// An engine change caused by adding/removing a provider should notify.
|
||||||
// any providers we receive are enabled in the AddonsManager
|
// any providers we receive are enabled in the AddonsManager
|
||||||
if (topic == "provider-added" || topic == "provider-removed") {
|
if (topic == "provider-installed" || topic == "provider-uninstalled") {
|
||||||
Social._updateProviderCache(data);
|
// installed/uninstalled do not send the providers param
|
||||||
|
Services.obs.notifyObservers(null, "social:" + topic, origin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (topic == "provider-enabled" || topic == "provider-disabled") {
|
||||||
|
Social._updateProviderCache(providers);
|
||||||
Social._updateWorkerState(true);
|
Social._updateWorkerState(true);
|
||||||
Services.obs.notifyObservers(null, "social:providers-changed", null);
|
Services.obs.notifyObservers(null, "social:providers-changed", null);
|
||||||
|
Services.obs.notifyObservers(null, "social:" + topic, origin);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (topic == "provider-update") {
|
if (topic == "provider-update") {
|
||||||
// a provider has self-updated its manifest, we need to update our cache
|
// a provider has self-updated its manifest, we need to update our cache
|
||||||
// and reload the provider.
|
// and reload the provider.
|
||||||
let provider = data;
|
Social._updateProviderCache(providers);
|
||||||
SocialService.getOrderedProviderList(function(providers) {
|
let provider = Social._getProviderFromOrigin(origin);
|
||||||
Social._updateProviderCache(providers);
|
provider.reload();
|
||||||
provider.reload();
|
Services.obs.notifyObservers(null, "social:providers-changed", null);
|
||||||
Services.obs.notifyObservers(null, "social:providers-changed", null);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -259,6 +263,10 @@ this.Social = {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getManifestByOrigin: function(origin) {
|
||||||
|
return SocialService.getManifestByOrigin(origin);
|
||||||
|
},
|
||||||
|
|
||||||
installProvider: function(doc, data, installCallback) {
|
installProvider: function(doc, data, installCallback) {
|
||||||
SocialService.installProvider(doc, data, installCallback);
|
SocialService.installProvider(doc, data, installCallback);
|
||||||
},
|
},
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ruleview-rule-inheritance {
|
.ruleview-header {
|
||||||
border-top-width: 1px;
|
border-top-width: 1px;
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
border-top-style: solid;
|
border-top-style: solid;
|
||||||
|
@ -14,16 +14,20 @@
|
|||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ruleview-rule-inheritance {
|
.ruleview-header {
|
||||||
border-top-width: 1px;
|
border-top-width: 1px;
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
border-top-style: solid;
|
border-top-style: solid;
|
||||||
border-bottom-style: solid;
|
border-bottom-style: solid;
|
||||||
padding: 1px 4px;
|
padding: 1px 4px;
|
||||||
margin-top: 4px;
|
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ruleview-rule-pseudo-element {
|
||||||
|
padding-left:20px;
|
||||||
|
border-left: solid 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.ruleview-rule-source:hover {
|
.ruleview-rule-source:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ruleview-rule-inheritance {
|
.ruleview-header {
|
||||||
border-top-width: 1px;
|
border-top-width: 1px;
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
border-top-style: solid;
|
border-top-style: solid;
|
||||||
|
@ -713,8 +713,8 @@ ImageDocument::UpdateTitleAndCharset()
|
|||||||
{
|
{
|
||||||
"ImageTitleWithNeitherDimensionsNorFile",
|
"ImageTitleWithNeitherDimensionsNorFile",
|
||||||
"ImageTitleWithoutDimensions",
|
"ImageTitleWithoutDimensions",
|
||||||
"ImageTitleWithDimensions",
|
"ImageTitleWithDimensions2",
|
||||||
"ImageTitleWithDimensionsAndFile",
|
"ImageTitleWithDimensions2AndFile",
|
||||||
};
|
};
|
||||||
|
|
||||||
MediaDocument::UpdateTitleAndCharset(typeStr, formatNames,
|
MediaDocument::UpdateTitleAndCharset(typeStr, formatNames,
|
||||||
|
@ -52,7 +52,7 @@ protected:
|
|||||||
// filename but w/o dimension, a format name with dimension but w/o filename,
|
// filename but w/o dimension, a format name with dimension but w/o filename,
|
||||||
// a format name with both of them. For instance, it can have
|
// a format name with both of them. For instance, it can have
|
||||||
// "ImageTitleWithNeitherDimensionsNorFile", "ImageTitleWithoutDimensions",
|
// "ImageTitleWithNeitherDimensionsNorFile", "ImageTitleWithoutDimensions",
|
||||||
// "ImageTitleWithDimesions", "ImageTitleWithDimensionsAndFile".
|
// "ImageTitleWithDimesions2", "ImageTitleWithDimensions2AndFile".
|
||||||
//
|
//
|
||||||
// Also see MediaDocument.properties if you want to define format names
|
// Also see MediaDocument.properties if you want to define format names
|
||||||
// for a new subclass. aWidth and aHeight are pixels for |ImageDocument|,
|
// for a new subclass. aWidth and aHeight are pixels for |ImageDocument|,
|
||||||
|
@ -646,7 +646,10 @@ BluetoothHfpManager::HandleIccInfoChanged()
|
|||||||
nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
|
nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
|
||||||
icc->GetIccInfo(getter_AddRefs(iccInfo));
|
icc->GetIccInfo(getter_AddRefs(iccInfo));
|
||||||
NS_ENSURE_TRUE_VOID(iccInfo);
|
NS_ENSURE_TRUE_VOID(iccInfo);
|
||||||
iccInfo->GetMsisdn(mMsisdn);
|
|
||||||
|
nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
|
||||||
|
NS_ENSURE_TRUE_VOID(gsmIccInfo);
|
||||||
|
gsmIccInfo->GetMsisdn(mMsisdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "mozilla/dom/ContentParent.h"
|
#include "mozilla/dom/ContentParent.h"
|
||||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||||
#include "mozilla/ipc/UnixSocket.h"
|
#include "mozilla/ipc/UnixSocket.h"
|
||||||
|
#include "mozilla/LazyIdleThread.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsCxPusher.h"
|
#include "nsCxPusher.h"
|
||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
@ -54,6 +55,7 @@
|
|||||||
|
|
||||||
#define PROP_BLUETOOTH_ENABLED "bluetooth.isEnabled"
|
#define PROP_BLUETOOTH_ENABLED "bluetooth.isEnabled"
|
||||||
|
|
||||||
|
#define DEFAULT_THREAD_TIMEOUT_MS 3000
|
||||||
#define DEFAULT_SHUTDOWN_TIMER_MS 5000
|
#define DEFAULT_SHUTDOWN_TIMER_MS 5000
|
||||||
|
|
||||||
bool gBluetoothDebugFlag = false;
|
bool gBluetoothDebugFlag = false;
|
||||||
@ -145,19 +147,8 @@ public:
|
|||||||
gBluetoothService->DistributeSignal(signal);
|
gBluetoothService->DistributeSignal(signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mEnabled || gInShutdown) {
|
if (gInShutdown) {
|
||||||
// Shut down the command thread if it still exists.
|
gBluetoothService = nullptr;
|
||||||
if (gBluetoothService->mBluetoothCommandThread) {
|
|
||||||
nsCOMPtr<nsIThread> thread;
|
|
||||||
gBluetoothService->mBluetoothCommandThread.swap(thread);
|
|
||||||
if (NS_FAILED(thread->Shutdown())) {
|
|
||||||
NS_WARNING("Failed to shut down the bluetooth command thread!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gInShutdown) {
|
|
||||||
gBluetoothService = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@ -461,22 +452,13 @@ BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup)
|
|||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mBluetoothCommandThread) {
|
if (!mBluetoothThread) {
|
||||||
// Don't create a new thread after we've begun shutdown since bluetooth
|
// Don't create a new thread after we've begun shutdown since bluetooth
|
||||||
// can't be running.
|
// can't be running.
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv;
|
|
||||||
if (!mBluetoothCommandThread) {
|
|
||||||
MOZ_ASSERT(!gInShutdown);
|
|
||||||
|
|
||||||
rv = NS_NewNamedThread("BluetoothCmd",
|
|
||||||
getter_AddRefs(mBluetoothCommandThread));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aStart) {
|
if (!aStart) {
|
||||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||||
hfp->Disconnect();
|
hfp->Disconnect();
|
||||||
@ -485,8 +467,14 @@ BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup)
|
|||||||
opp->Disconnect();
|
opp->Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mBluetoothThread) {
|
||||||
|
mBluetoothThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
|
||||||
|
NS_LITERAL_CSTRING("Bluetooth"),
|
||||||
|
LazyIdleThread::ManualShutdown);
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = new ToggleBtTask(aStart, aIsStartup);
|
nsCOMPtr<nsIRunnable> runnable = new ToggleBtTask(aStart, aIsStartup);
|
||||||
rv = mBluetoothCommandThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
nsresult rv = mBluetoothThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@ -814,13 +802,3 @@ BluetoothService::Notify(const BluetoothSignal& aData)
|
|||||||
OBJECT_TO_JSVAL(obj),
|
OBJECT_TO_JSVAL(obj),
|
||||||
JS::UndefinedValue());
|
JS::UndefinedValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
BluetoothService::DispatchToCommandThread(nsRunnable* aRunnable)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
MOZ_ASSERT(aRunnable);
|
|
||||||
MOZ_ASSERT(mBluetoothCommandThread);
|
|
||||||
|
|
||||||
mBluetoothCommandThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
|
|
||||||
}
|
|
||||||
|
@ -47,7 +47,7 @@ public:
|
|||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
NS_DECL_NSIOBSERVER
|
NS_DECL_NSIOBSERVER
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a message handler object from message distribution observer.
|
* Add a message handler object from message distribution observer.
|
||||||
* Must be called from the main thread.
|
* Must be called from the main thread.
|
||||||
*
|
*
|
||||||
@ -58,7 +58,7 @@ public:
|
|||||||
RegisterBluetoothSignalHandler(const nsAString& aNodeName,
|
RegisterBluetoothSignalHandler(const nsAString& aNodeName,
|
||||||
BluetoothSignalObserver* aMsgHandler);
|
BluetoothSignalObserver* aMsgHandler);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a message handler object from message distribution observer.
|
* Remove a message handler object from message distribution observer.
|
||||||
* Must be called from the main thread.
|
* Must be called from the main thread.
|
||||||
*
|
*
|
||||||
@ -69,7 +69,7 @@ public:
|
|||||||
UnregisterBluetoothSignalHandler(const nsAString& aNodeName,
|
UnregisterBluetoothSignalHandler(const nsAString& aNodeName,
|
||||||
BluetoothSignalObserver* aMsgHandler);
|
BluetoothSignalObserver* aMsgHandler);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a message handlers for the given observer.
|
* Remove a message handlers for the given observer.
|
||||||
* Must be called from the main thread.
|
* Must be called from the main thread.
|
||||||
*
|
*
|
||||||
@ -78,7 +78,7 @@ public:
|
|||||||
void
|
void
|
||||||
UnregisterAllSignalHandlers(BluetoothSignalObserver* aMsgHandler);
|
UnregisterAllSignalHandlers(BluetoothSignalObserver* aMsgHandler);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Distribute a signal to the observer list
|
* Distribute a signal to the observer list
|
||||||
*
|
*
|
||||||
* @param aSignal Signal object to distribute
|
* @param aSignal Signal object to distribute
|
||||||
@ -150,7 +150,7 @@ public:
|
|||||||
virtual nsresult
|
virtual nsresult
|
||||||
StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) = 0;
|
StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start device discovery (platform specific implementation)
|
* Start device discovery (platform specific implementation)
|
||||||
*
|
*
|
||||||
* @return NS_OK if discovery stopped correctly, false otherwise
|
* @return NS_OK if discovery stopped correctly, false otherwise
|
||||||
@ -307,9 +307,6 @@ public:
|
|||||||
void
|
void
|
||||||
RemoveObserverFromTable(const nsAString& key);
|
RemoveObserverFromTable(const nsAString& key);
|
||||||
|
|
||||||
void
|
|
||||||
DispatchToCommandThread(nsRunnable* aRunnable);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
BluetoothService()
|
BluetoothService()
|
||||||
: mEnabled(false)
|
: mEnabled(false)
|
||||||
@ -328,7 +325,7 @@ protected:
|
|||||||
nsresult
|
nsresult
|
||||||
StartStopBluetooth(bool aStart, bool aIsStartup);
|
StartStopBluetooth(bool aStart, bool aIsStartup);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Platform specific startup functions go here. Usually deals with member
|
* Platform specific startup functions go here. Usually deals with member
|
||||||
* variables, so not static. Guaranteed to be called outside of main thread.
|
* variables, so not static. Guaranteed to be called outside of main thread.
|
||||||
*
|
*
|
||||||
@ -387,26 +384,21 @@ protected:
|
|||||||
static BluetoothService*
|
static BluetoothService*
|
||||||
Create();
|
Create();
|
||||||
|
|
||||||
/**
|
|
||||||
* Due to the fact that some operations require multiple calls, a
|
|
||||||
* CommandThread is created that can run blocking, platform-specific calls
|
|
||||||
* where either no asynchronous equivilent exists, or else where multiple
|
|
||||||
* asynchronous calls would require excessive runnable bouncing between main
|
|
||||||
* thread and IO thread.
|
|
||||||
*
|
|
||||||
* For instance, when we retrieve an Adapter object, we would like it to come
|
|
||||||
* with all of its properties filled in and registered as an agent, which
|
|
||||||
* requires a minimum of 3 calls to platform specific code on some platforms.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
nsCOMPtr<nsIThread> mBluetoothCommandThread;
|
|
||||||
|
|
||||||
typedef nsClassHashtable<nsStringHashKey, BluetoothSignalObserverList >
|
typedef nsClassHashtable<nsStringHashKey, BluetoothSignalObserverList >
|
||||||
BluetoothSignalObserverTable;
|
BluetoothSignalObserverTable;
|
||||||
|
|
||||||
BluetoothSignalObserverTable mBluetoothSignalObserverTable;
|
BluetoothSignalObserverTable mBluetoothSignalObserverTable;
|
||||||
|
|
||||||
bool mEnabled;
|
bool mEnabled;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Due to the fact that the startup and shutdown of the Bluetooth system
|
||||||
|
* can take an indefinite amount of time, a command thread is created
|
||||||
|
* that can run blocking calls. The thread is not intended for regular
|
||||||
|
* Bluetooth operations though.
|
||||||
|
*/
|
||||||
|
nsCOMPtr<nsIThread> mBluetoothThread;
|
||||||
};
|
};
|
||||||
|
|
||||||
END_BLUETOOTH_NAMESPACE
|
END_BLUETOOTH_NAMESPACE
|
||||||
|
@ -183,6 +183,12 @@ const ContentPanning = {
|
|||||||
|
|
||||||
this.position.set(screenX, screenY);
|
this.position.set(screenX, screenY);
|
||||||
KineticPanning.record(new Point(0, 0), evt.timeStamp);
|
KineticPanning.record(new Point(0, 0), evt.timeStamp);
|
||||||
|
|
||||||
|
// We prevent start events to avoid sending a focus event at the end of this
|
||||||
|
// touch series. See bug 889717.
|
||||||
|
if (this.panning || this.preventNextClick) {
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onTouchEnd: function cp_onTouchEnd(evt) {
|
onTouchEnd: function cp_onTouchEnd(evt) {
|
||||||
@ -214,10 +220,15 @@ const ContentPanning = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.target && click && (this.panning || this.preventNextClick)) {
|
if (this.target && click && (this.panning || this.preventNextClick)) {
|
||||||
let target = this.target;
|
if (this.hybridEvents) {
|
||||||
let view = target.ownerDocument ? target.ownerDocument.defaultView
|
let target = this.target;
|
||||||
: target;
|
let view = target.ownerDocument ? target.ownerDocument.defaultView
|
||||||
view.addEventListener('click', this, true, true);
|
: target;
|
||||||
|
view.addEventListener('click', this, true, true);
|
||||||
|
} else {
|
||||||
|
// We prevent end events to avoid sending a focus event. See bug 889717.
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._finishPanning();
|
this._finishPanning();
|
||||||
|
@ -4,10 +4,17 @@
|
|||||||
|
|
||||||
#include "nsISupports.idl"
|
#include "nsISupports.idl"
|
||||||
|
|
||||||
[scriptable, uuid(a45c0fe0-c911-11e2-8b8b-0800200c9a66)]
|
[scriptable, uuid(dd9f229c-e5a6-453a-8388-950af0ff9918)]
|
||||||
interface nsIDOMMozIccInfo : nsISupports
|
interface nsIDOMMozIccInfo : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
* Integrated Circuit Card Type.
|
||||||
|
*
|
||||||
|
* Possible values: "sim", "usim", "ruim".
|
||||||
|
*/
|
||||||
|
readonly attribute DOMString iccType;
|
||||||
|
|
||||||
|
/**
|
||||||
* Integrated Circuit Card Identifier.
|
* Integrated Circuit Card Identifier.
|
||||||
*/
|
*/
|
||||||
readonly attribute DOMString iccid;
|
readonly attribute DOMString iccid;
|
||||||
@ -36,10 +43,28 @@ interface nsIDOMMozIccInfo : nsISupports
|
|||||||
* Service provider name must be a part of displayed carrier name.
|
* Service provider name must be a part of displayed carrier name.
|
||||||
*/
|
*/
|
||||||
readonly attribute boolean isDisplaySpnRequired;
|
readonly attribute boolean isDisplaySpnRequired;
|
||||||
|
};
|
||||||
|
|
||||||
|
[scriptable, uuid(3c237e39-7af3-4748-baf4-4a3b6c3e0e66)]
|
||||||
|
interface nsIDOMMozGsmIccInfo : nsIDOMMozIccInfo
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* Mobile Station ISDN Number (MSISDN) of the subscriber's, aka
|
* Mobile Station ISDN Number (MSISDN) of the subscriber, aka
|
||||||
* his phone number.
|
* his phone number.
|
||||||
*/
|
*/
|
||||||
readonly attribute DOMString msisdn;
|
readonly attribute DOMString msisdn;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[scriptable, uuid(013e973e-8b56-4525-b634-d23166b86edb)]
|
||||||
|
interface nsIDOMMozCdmaIccInfo : nsIDOMMozIccInfo
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Mobile Directory Number (MDN) of the subscriber, aka his phone number.
|
||||||
|
*/
|
||||||
|
readonly attribute DOMString mdn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mobile Identification Number (MIN) of the subscriber.
|
||||||
|
*/
|
||||||
|
readonly attribute DOMString min;
|
||||||
|
};
|
||||||
|
@ -280,6 +280,8 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Information stored in the device's ICC card.
|
* Information stored in the device's ICC card.
|
||||||
|
*
|
||||||
|
* Null if the card is not detected.
|
||||||
*/
|
*/
|
||||||
readonly attribute nsIDOMMozIccInfo iccInfo;
|
readonly attribute nsIDOMMozIccInfo iccInfo;
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ ifr.onload = function() {
|
|||||||
|
|
||||||
iccInfo = icc.iccInfo;
|
iccInfo = icc.iccInfo;
|
||||||
|
|
||||||
|
is(iccInfo.iccType, "sim");
|
||||||
|
|
||||||
// The emulator's hard coded iccid value.
|
// The emulator's hard coded iccid value.
|
||||||
// See it here {B2G_HOME}/external/qemu/telephony/sim_card.c#L299.
|
// See it here {B2G_HOME}/external/qemu/telephony/sim_card.c#L299.
|
||||||
is(iccInfo.iccid, 89014103211118510720);
|
is(iccInfo.iccid, 89014103211118510720);
|
||||||
@ -30,15 +32,7 @@ ifr.onload = function() {
|
|||||||
// See {B2G_HOME}/external/qemu/telephony/sim_card.c, in asimcard_io()
|
// See {B2G_HOME}/external/qemu/telephony/sim_card.c, in asimcard_io()
|
||||||
is(iccInfo.msisdn, "15555215554");
|
is(iccInfo.msisdn, "15555215554");
|
||||||
|
|
||||||
testDisplayConditionChange(testSPN, [
|
runNextTest();
|
||||||
// [MCC, MNC, isDisplayNetworkNameRequired, isDisplaySpnRequired]
|
|
||||||
[123, 456, false, true], // Not in HPLMN.
|
|
||||||
[234, 136, true, true], // Not in HPLMN, but in PLMN specified in SPDI.
|
|
||||||
[123, 456, false, true], // Not in HPLMN. Triggering iccinfochange
|
|
||||||
[466, 92, true, true], // Not in HPLMN, but in another PLMN specified in SPDI.
|
|
||||||
[123, 456, false, true], // Not in HPLMN. Triggering iccinfochange
|
|
||||||
[310, 260, true, true], // inside HPLMN.
|
|
||||||
], finalize);
|
|
||||||
};
|
};
|
||||||
document.body.appendChild(ifr);
|
document.body.appendChild(ifr);
|
||||||
|
|
||||||
@ -60,6 +54,25 @@ function setEmulatorMccMnc(mcc, mnc) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setAirplaneModeEnabled(enabled) {
|
||||||
|
let settings = ifr.contentWindow.navigator.mozSettings;
|
||||||
|
let setLock = settings.createLock();
|
||||||
|
let obj = {
|
||||||
|
"ril.radio.disabled": enabled
|
||||||
|
};
|
||||||
|
let setReq = setLock.set(obj);
|
||||||
|
|
||||||
|
log("set airplane mode to " + enabled);
|
||||||
|
|
||||||
|
setReq.addEventListener("success", function onSetSuccess() {
|
||||||
|
log("set 'ril.radio.disabled' to " + enabled);
|
||||||
|
});
|
||||||
|
|
||||||
|
setReq.addEventListener("error", function onSetError() {
|
||||||
|
ok(false, "cannot set 'ril.radio.disabled' to " + enabled);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function waitForIccInfoChange(callback) {
|
function waitForIccInfoChange(callback) {
|
||||||
icc.addEventListener("iccinfochange", function handler() {
|
icc.addEventListener("iccinfochange", function handler() {
|
||||||
icc.removeEventListener("iccinfochange", handler);
|
icc.removeEventListener("iccinfochange", handler);
|
||||||
@ -67,9 +80,15 @@ function waitForIccInfoChange(callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function finalize() {
|
function waitForCardStateChange(expectedCardState, callback) {
|
||||||
SpecialPowers.removePermission("mobileconnection", document);
|
icc.addEventListener("cardstatechange", function oncardstatechange() {
|
||||||
finish();
|
log("card state changes to " + icc.cardState);
|
||||||
|
if (icc.cardState === expectedCardState) {
|
||||||
|
log("got expected card state: " + icc.cardState);
|
||||||
|
icc.removeEventListener("cardstatechange", oncardstatechange);
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test display condition change.
|
// Test display condition change.
|
||||||
@ -93,3 +112,45 @@ function testSPN(mcc, mnc, expectedIsDisplayNetworkNameRequired,
|
|||||||
});
|
});
|
||||||
setEmulatorMccMnc(mcc, mnc);
|
setEmulatorMccMnc(mcc, mnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test iccInfo when card is not ready
|
||||||
|
function testCardIsNotReady() {
|
||||||
|
// Enable airplane mode
|
||||||
|
setAirplaneModeEnabled(true);
|
||||||
|
|
||||||
|
waitForCardStateChange(null, function callback() {
|
||||||
|
is(icc.iccInfo, null);
|
||||||
|
|
||||||
|
// Disable airplane mode
|
||||||
|
setAirplaneModeEnabled(false);
|
||||||
|
waitForCardStateChange("ready", runNextTest);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let tests = [
|
||||||
|
testDisplayConditionChange.bind(this, testSPN, [
|
||||||
|
// [MCC, MNC, isDisplayNetworkNameRequired, isDisplaySpnRequired]
|
||||||
|
[123, 456, false, true], // Not in HPLMN.
|
||||||
|
[234, 136, true, true], // Not in HPLMN, but in PLMN specified in SPDI.
|
||||||
|
[123, 456, false, true], // Not in HPLMN. Triggering iccinfochange
|
||||||
|
[466, 92, true, true], // Not in HPLMN, but in another PLMN specified in SPDI.
|
||||||
|
[123, 456, false, true], // Not in HPLMN. Triggering iccinfochange
|
||||||
|
[310, 260, true, true], // inside HPLMN.
|
||||||
|
], runNextTest),
|
||||||
|
testCardIsNotReady
|
||||||
|
];
|
||||||
|
|
||||||
|
function runNextTest() {
|
||||||
|
let test = tests.shift();
|
||||||
|
if (!test) {
|
||||||
|
finalize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
test();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finalize() {
|
||||||
|
SpecialPowers.removePermission("mobileconnection", document);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
#LOCALIZATION NOTE (ImageTitleWithDimensionsAndFile): first %S is filename, second %S is type, third %S is width and fourth %S is height
|
#LOCALIZATION NOTE (ImageTitleWithDimensions2AndFile): first %S is filename, second %S is type, third %S is width and fourth %S is height
|
||||||
#LOCALIZATION NOTE (ImageTitleWithoutDimensions): first %S is filename, second %S is type
|
#LOCALIZATION NOTE (ImageTitleWithoutDimensions): first %S is filename, second %S is type
|
||||||
#LOCALIZATION NOTE (ImageTitleWithDimensions): first %S is type, second %S is width and third %S is height
|
#LOCALIZATION NOTE (ImageTitleWithDimensions2): first %S is type, second %S is width and third %S is height
|
||||||
#LOCALIZATION NOTE (ImageTitleWithNeitherDimensionsNorFile): first %S is type
|
#LOCALIZATION NOTE (ImageTitleWithNeitherDimensionsNorFile): first %S is type
|
||||||
#LOCALIZATION NOTE (MediaTitleWithFile): first %S is filename, second %S is type
|
#LOCALIZATION NOTE (MediaTitleWithFile): first %S is filename, second %S is type
|
||||||
#LOCALIZATION NOTE (MediaTitleWithNoInfo): first %S is type
|
#LOCALIZATION NOTE (MediaTitleWithNoInfo): first %S is type
|
||||||
ImageTitleWithDimensionsAndFile=%S (%S Image, %S\u00A0\u00D7\u00A0%S pixels)
|
ImageTitleWithDimensions2AndFile=%S (%S Image, %S\u00A0\u00D7\u00A0%S pixels)
|
||||||
ImageTitleWithoutDimensions=%S (%S Image)
|
ImageTitleWithoutDimensions=%S (%S Image)
|
||||||
ImageTitleWithDimensions=(%S Image, %S\u00A0\u00D7\u00A0%S pixels)
|
ImageTitleWithDimensions2=(%S Image, %S\u00A0\u00D7\u00A0%S pixels)
|
||||||
ImageTitleWithNeitherDimensionsNorFile=(%S Image)
|
ImageTitleWithNeitherDimensionsNorFile=(%S Image)
|
||||||
MediaTitleWithFile=%S (%S Object)
|
MediaTitleWithFile=%S (%S Object)
|
||||||
MediaTitleWithNoInfo=(%S Object)
|
MediaTitleWithNoInfo=(%S Object)
|
||||||
|
@ -11,10 +11,10 @@ interface nsIDOMDOMCursor;
|
|||||||
interface nsIDOMDOMRequest;
|
interface nsIDOMDOMRequest;
|
||||||
interface nsIDOMBlob;
|
interface nsIDOMBlob;
|
||||||
|
|
||||||
[scriptable, builtinclass, uuid(efff5276-0f3f-4137-9b16-66e894400e01)]
|
[scriptable, builtinclass, uuid(3f81dcbc-00cf-11e3-ae66-538115636543)]
|
||||||
interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget
|
interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget
|
||||||
{
|
{
|
||||||
nsIDOMMozSmsSegmentInfo getSegmentInfoForText(in DOMString text);
|
nsIDOMDOMRequest getSegmentInfoForText(in DOMString text);
|
||||||
|
|
||||||
// The first parameter can be either a DOMString (only one number) or an array
|
// The first parameter can be either a DOMString (only one number) or an array
|
||||||
// of DOMStrings.
|
// of DOMStrings.
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "nsISupports.idl"
|
#include "nsISupports.idl"
|
||||||
|
#include "nsIDOMSmsSegmentInfo.idl"
|
||||||
|
|
||||||
dictionary SmsThreadListItem
|
dictionary SmsThreadListItem
|
||||||
{
|
{
|
||||||
@ -13,7 +14,7 @@ dictionary SmsThreadListItem
|
|||||||
unsigned long long unreadCount;
|
unsigned long long unreadCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
[scriptable, uuid(ea5fb581-bee7-40a6-b2dc-c98b99a2dc49)]
|
[scriptable, uuid(399125a8-00d2-11e3-8d12-3fba4465c097)]
|
||||||
interface nsIMobileMessageCallback : nsISupports
|
interface nsIMobileMessageCallback : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -48,4 +49,7 @@ interface nsIMobileMessageCallback : nsISupports
|
|||||||
|
|
||||||
void notifyMessageMarkedRead(in boolean read);
|
void notifyMessageMarkedRead(in boolean read);
|
||||||
void notifyMarkMessageReadFailed(in long error);
|
void notifyMarkMessageReadFailed(in long error);
|
||||||
|
|
||||||
|
void notifySegmentInfoForTextGot(in nsIDOMMozSmsSegmentInfo info);
|
||||||
|
void notifyGetSegmentInfoForTextFailed(in long error);
|
||||||
};
|
};
|
||||||
|
@ -13,12 +13,13 @@ interface nsIMobileMessageCallback;
|
|||||||
#define SMS_SERVICE_CONTRACTID "@mozilla.org/sms/smsservice;1"
|
#define SMS_SERVICE_CONTRACTID "@mozilla.org/sms/smsservice;1"
|
||||||
%}
|
%}
|
||||||
|
|
||||||
[scriptable, builtinclass, uuid(f0d5d11b-0326-4cb1-bb76-a3f912212287)]
|
[scriptable, builtinclass, uuid(0f3f75ec-00dd-11e3-87ac-0b1d5c79afdf)]
|
||||||
interface nsISmsService : nsISupports
|
interface nsISmsService : nsISupports
|
||||||
{
|
{
|
||||||
boolean hasSupport();
|
boolean hasSupport();
|
||||||
|
|
||||||
nsIDOMMozSmsSegmentInfo getSegmentInfoForText(in DOMString text);
|
void getSegmentInfoForText(in DOMString text,
|
||||||
|
in nsIMobileMessageCallback request);
|
||||||
|
|
||||||
void send(in DOMString number,
|
void send(in DOMString number,
|
||||||
in DOMString message,
|
in DOMString message,
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "nsCxPusher.h"
|
#include "nsCxPusher.h"
|
||||||
#include "nsIDOMMozSmsMessage.h"
|
#include "nsIDOMMozSmsMessage.h"
|
||||||
#include "nsIDOMMozMmsMessage.h"
|
#include "nsIDOMMozMmsMessage.h"
|
||||||
|
#include "nsIDOMSmsSegmentInfo.h"
|
||||||
#include "nsIScriptGlobalObject.h"
|
#include "nsIScriptGlobalObject.h"
|
||||||
#include "nsPIDOMWindow.h"
|
#include "nsPIDOMWindow.h"
|
||||||
#include "MmsMessage.h"
|
#include "MmsMessage.h"
|
||||||
@ -39,14 +40,22 @@ MobileMessageCallback::~MobileMessageCallback()
|
|||||||
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
MobileMessageCallback::NotifySuccess(JS::Handle<JS::Value> aResult)
|
MobileMessageCallback::NotifySuccess(JS::Handle<JS::Value> aResult, bool aAsync)
|
||||||
{
|
{
|
||||||
|
if (aAsync) {
|
||||||
|
nsCOMPtr<nsIDOMRequestService> rs =
|
||||||
|
do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
|
||||||
|
NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
return rs->FireSuccessAsync(mDOMRequest, aResult);
|
||||||
|
}
|
||||||
|
|
||||||
mDOMRequest->FireSuccess(aResult);
|
mDOMRequest->FireSuccess(aResult);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
MobileMessageCallback::NotifySuccess(nsISupports *aMessage)
|
MobileMessageCallback::NotifySuccess(nsISupports *aMessage, bool aAsync)
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
nsIScriptContext* scriptContext = mDOMRequest->GetContextForEventHandlers(&rv);
|
nsIScriptContext* scriptContext = mDOMRequest->GetContextForEventHandlers(&rv);
|
||||||
@ -66,38 +75,48 @@ MobileMessageCallback::NotifySuccess(nsISupports *aMessage)
|
|||||||
wrappedMessage.address());
|
wrappedMessage.address());
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
return NotifySuccess(wrappedMessage);
|
return NotifySuccess(wrappedMessage, aAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
MobileMessageCallback::NotifyError(int32_t aError)
|
MobileMessageCallback::NotifyError(int32_t aError, bool aAsync)
|
||||||
{
|
{
|
||||||
|
nsAutoString errorStr;
|
||||||
switch (aError) {
|
switch (aError) {
|
||||||
case nsIMobileMessageCallback::NO_SIGNAL_ERROR:
|
case nsIMobileMessageCallback::NO_SIGNAL_ERROR:
|
||||||
mDOMRequest->FireError(NS_LITERAL_STRING("NoSignalError"));
|
errorStr = NS_LITERAL_STRING("NoSignalError");
|
||||||
break;
|
break;
|
||||||
case nsIMobileMessageCallback::NOT_FOUND_ERROR:
|
case nsIMobileMessageCallback::NOT_FOUND_ERROR:
|
||||||
mDOMRequest->FireError(NS_LITERAL_STRING("NotFoundError"));
|
errorStr = NS_LITERAL_STRING("NotFoundError");
|
||||||
break;
|
break;
|
||||||
case nsIMobileMessageCallback::UNKNOWN_ERROR:
|
case nsIMobileMessageCallback::UNKNOWN_ERROR:
|
||||||
mDOMRequest->FireError(NS_LITERAL_STRING("UnknownError"));
|
errorStr = NS_LITERAL_STRING("UnknownError");
|
||||||
break;
|
break;
|
||||||
case nsIMobileMessageCallback::INTERNAL_ERROR:
|
case nsIMobileMessageCallback::INTERNAL_ERROR:
|
||||||
mDOMRequest->FireError(NS_LITERAL_STRING("InternalError"));
|
errorStr = NS_LITERAL_STRING("InternalError");
|
||||||
break;
|
break;
|
||||||
case nsIMobileMessageCallback::NO_SIM_CARD_ERROR:
|
case nsIMobileMessageCallback::NO_SIM_CARD_ERROR:
|
||||||
mDOMRequest->FireError(NS_LITERAL_STRING("NoSimCardError"));
|
errorStr = NS_LITERAL_STRING("NoSimCardError");
|
||||||
break;
|
break;
|
||||||
case nsIMobileMessageCallback::RADIO_DISABLED_ERROR:
|
case nsIMobileMessageCallback::RADIO_DISABLED_ERROR:
|
||||||
mDOMRequest->FireError(NS_LITERAL_STRING("RadioDisabledError"));
|
errorStr = NS_LITERAL_STRING("RadioDisabledError");
|
||||||
break;
|
break;
|
||||||
case nsIMobileMessageCallback::INVALID_ADDRESS_ERROR:
|
case nsIMobileMessageCallback::INVALID_ADDRESS_ERROR:
|
||||||
mDOMRequest->FireError(NS_LITERAL_STRING("InvalidAddressError"));
|
errorStr = NS_LITERAL_STRING("InvalidAddressError");
|
||||||
break;
|
break;
|
||||||
default: // SUCCESS_NO_ERROR is handled above.
|
default: // SUCCESS_NO_ERROR is handled above.
|
||||||
MOZ_CRASH("Should never get here!");
|
MOZ_CRASH("Should never get here!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aAsync) {
|
||||||
|
nsCOMPtr<nsIDOMRequestService> rs =
|
||||||
|
do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
|
||||||
|
NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
return rs->FireErrorAsync(mDOMRequest, errorStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
mDOMRequest->FireError(errorStr);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +192,18 @@ MobileMessageCallback::NotifyMarkMessageReadFailed(int32_t aError)
|
|||||||
return NotifyError(aError);
|
return NotifyError(aError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
MobileMessageCallback::NotifySegmentInfoForTextGot(nsIDOMMozSmsSegmentInfo *aInfo)
|
||||||
|
{
|
||||||
|
return NotifySuccess(aInfo, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
MobileMessageCallback::NotifyGetSegmentInfoForTextFailed(int32_t aError)
|
||||||
|
{
|
||||||
|
return NotifyError(aError, true);
|
||||||
|
}
|
||||||
|
|
||||||
} // namesapce mobilemessage
|
} // namesapce mobilemessage
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -29,9 +29,9 @@ private:
|
|||||||
|
|
||||||
nsRefPtr<DOMRequest> mDOMRequest;
|
nsRefPtr<DOMRequest> mDOMRequest;
|
||||||
|
|
||||||
nsresult NotifySuccess(JS::Handle<JS::Value> aResult);
|
nsresult NotifySuccess(JS::Handle<JS::Value> aResult, bool aAsync = false);
|
||||||
nsresult NotifySuccess(nsISupports *aMessage);
|
nsresult NotifySuccess(nsISupports *aMessage, bool aAsync = false);
|
||||||
nsresult NotifyError(int32_t aError);
|
nsresult NotifyError(int32_t aError, bool aAsync = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mobilemessage
|
} // namespace mobilemessage
|
||||||
|
@ -101,12 +101,19 @@ MobileMessageManager::Shutdown()
|
|||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
MobileMessageManager::GetSegmentInfoForText(const nsAString& aText,
|
MobileMessageManager::GetSegmentInfoForText(const nsAString& aText,
|
||||||
nsIDOMMozSmsSegmentInfo** aResult)
|
nsIDOMDOMRequest** aRequest)
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
|
nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
|
||||||
NS_ENSURE_TRUE(smsService, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(smsService, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
return smsService->GetSegmentInfoForText(aText, aResult);
|
nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
|
||||||
|
nsCOMPtr<nsIMobileMessageCallback> msgCallback =
|
||||||
|
new MobileMessageCallback(request);
|
||||||
|
nsresult rv = smsService->GetSegmentInfoForText(aText, msgCallback);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
request.forget(aRequest);
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
@ -55,5 +55,11 @@ SmsSegmentInfo::GetCharsAvailableInLastSegment(int32_t* aCharsAvailableInLastSeg
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SmsSegmentInfoData&
|
||||||
|
SmsSegmentInfo::GetData() const
|
||||||
|
{
|
||||||
|
return mData;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -22,8 +22,11 @@ public:
|
|||||||
SmsSegmentInfo(int32_t aSegments,
|
SmsSegmentInfo(int32_t aSegments,
|
||||||
int32_t aCharsPerSegment,
|
int32_t aCharsPerSegment,
|
||||||
int32_t aCharsAvailableInLastSegment);
|
int32_t aCharsAvailableInLastSegment);
|
||||||
|
|
||||||
SmsSegmentInfo(const mobilemessage::SmsSegmentInfoData& aData);
|
SmsSegmentInfo(const mobilemessage::SmsSegmentInfoData& aData);
|
||||||
|
|
||||||
|
const mobilemessage::SmsSegmentInfoData& GetData() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mobilemessage::SmsSegmentInfoData mData;
|
mobilemessage::SmsSegmentInfoData mData;
|
||||||
};
|
};
|
||||||
|
@ -23,19 +23,16 @@ SmsService::HasSupport(bool* aHasSupport)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
SmsService::GetSegmentInfoForText(const nsAString & aText,
|
SmsService::GetSegmentInfoForText(const nsAString& aText,
|
||||||
nsIDOMMozSmsSegmentInfo** aResult)
|
nsIMobileMessageCallback* aRequest)
|
||||||
{
|
{
|
||||||
if (!AndroidBridge::Bridge()) {
|
if (!AndroidBridge::Bridge()) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
SmsSegmentInfoData data;
|
nsresult rv = AndroidBridge::Bridge()->GetSegmentInfoForText(aText, aRequest);
|
||||||
nsresult rv = AndroidBridge::Bridge()->GetSegmentInfoForText(aText, &data);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMMozSmsSegmentInfo> info = new SmsSegmentInfo(data);
|
|
||||||
info.forget(aResult);
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,8 +22,8 @@ SmsService::HasSupport(bool* aHasSupport)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
SmsService::GetSegmentInfoForText(const nsAString & aText,
|
SmsService::GetSegmentInfoForText(const nsAString& aText,
|
||||||
nsIDOMMozSmsSegmentInfo** aResult)
|
nsIMobileMessageCallback* aRequest)
|
||||||
{
|
{
|
||||||
NS_ERROR("We should not be here!");
|
NS_ERROR("We should not be here!");
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
@ -11,6 +11,8 @@ Cu.import("resource://gre/modules/WspPduHelper.jsm", WSP);
|
|||||||
|
|
||||||
Cu.import("resource://gre/modules/mms_consts.js");
|
Cu.import("resource://gre/modules/mms_consts.js");
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
|
||||||
|
|
||||||
let DEBUG; // set to true to see debug messages
|
let DEBUG; // set to true to see debug messages
|
||||||
|
|
||||||
this.MMS_VERSION = (function () {
|
this.MMS_VERSION = (function () {
|
||||||
@ -178,6 +180,33 @@ this.Address = {
|
|||||||
|
|
||||||
EncodedStringValue.encode(data, str);
|
EncodedStringValue.encode(data, str);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param address
|
||||||
|
* Address string which want to find the type.
|
||||||
|
*
|
||||||
|
* @return Address type.
|
||||||
|
*/
|
||||||
|
resolveType: function resolveType(address) {
|
||||||
|
if (address.match(this.REGEXP_EMAIL)) {
|
||||||
|
return "email";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address.match(this.REGEXP_IPV4)) {
|
||||||
|
return "IPv4";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address.match(this.REGEXP_IPV6)) {
|
||||||
|
return "IPv6";
|
||||||
|
}
|
||||||
|
|
||||||
|
let normalizedAddress = PhoneNumberUtils.normalize(address, false);
|
||||||
|
if (PhoneNumberUtils.isPlainPhoneNumber(normalizedAddress)) {
|
||||||
|
return "PLMN";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Others";
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
defineLazyRegExp(Address, "REGEXP_DECODE_PLMN", "^(\\+?[\\d.-]+)\\/TYPE=(PLMN)$");
|
defineLazyRegExp(Address, "REGEXP_DECODE_PLMN", "^(\\+?[\\d.-]+)\\/TYPE=(PLMN)$");
|
||||||
@ -191,6 +220,10 @@ defineLazyRegExp(Address, "REGEXP_ENCODE_CUSTOM_TYPE", "^\\w+$");
|
|||||||
defineLazyRegExp(Address, "REGEXP_ENCODE_CUSTOM_ADDR", "^[\\w\\+\\-.%]+$");
|
defineLazyRegExp(Address, "REGEXP_ENCODE_CUSTOM_ADDR", "^[\\w\\+\\-.%]+$");
|
||||||
defineLazyRegExp(Address, "REGEXP_NUM", "^[\\+*#]\\d+$");
|
defineLazyRegExp(Address, "REGEXP_NUM", "^[\\+*#]\\d+$");
|
||||||
defineLazyRegExp(Address, "REGEXP_ALPHANUM", "^\\w+$");
|
defineLazyRegExp(Address, "REGEXP_ALPHANUM", "^\\w+$");
|
||||||
|
defineLazyRegExp(Address, "REGEXP_PLMN", "^\\?[\\d.-]$");
|
||||||
|
defineLazyRegExp(Address, "REGEXP_IPV4", "^\\d{1,3}(?:\\.\\d{1,3}){3}$");
|
||||||
|
defineLazyRegExp(Address, "REGEXP_IPV6", "^[\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7}$");
|
||||||
|
defineLazyRegExp(Address, "REGEXP_EMAIL", "@");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Header-field = MMS-header | Application-header
|
* Header-field = MMS-header | Application-header
|
||||||
|
@ -1687,18 +1687,22 @@ MmsService.prototype = {
|
|||||||
if (receivers.length != 0) {
|
if (receivers.length != 0) {
|
||||||
let headersTo = headers["to"] = [];
|
let headersTo = headers["to"] = [];
|
||||||
for (let i = 0; i < receivers.length; i++) {
|
for (let i = 0; i < receivers.length; i++) {
|
||||||
let normalizedAddress = PhoneNumberUtils.normalize(receivers[i], false);
|
let receiver = receivers[i];
|
||||||
if (DEBUG) debug("createSavableFromParams: normalize phone number " +
|
let type = MMS.Address.resolveType(receiver);
|
||||||
"from " + receivers[i] + " to " + normalizedAddress);
|
let address;
|
||||||
|
if (type == "PLMN") {
|
||||||
headersTo.push({"address": normalizedAddress, "type": "PLMN"});
|
address = PhoneNumberUtils.normalize(receiver, false);
|
||||||
|
if (!PhoneNumberUtils.isPlainPhoneNumber(address)) {
|
||||||
// Check if the address is valid to send MMS.
|
isAddrValid = false;
|
||||||
if (!PhoneNumberUtils.isPlainPhoneNumber(normalizedAddress)) {
|
}
|
||||||
if (DEBUG) debug("Error! Address is invalid to send MMS: " +
|
if (DEBUG) debug("createSavableFromParams: normalize phone number " +
|
||||||
normalizedAddress);
|
"from " + receiver + " to " + address);
|
||||||
|
} else {
|
||||||
|
address = receiver;
|
||||||
isAddrValid = false;
|
isAddrValid = false;
|
||||||
|
if (DEBUG) debug("Error! Address is invalid to send MMS: " + address);
|
||||||
}
|
}
|
||||||
|
headersTo.push({"address": address, "type": type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (aParams.subject) {
|
if (aParams.subject) {
|
||||||
|
@ -31,12 +31,12 @@ SmsService::HasSupport(bool* aHasSupport)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
SmsService::GetSegmentInfoForText(const nsAString & aText,
|
SmsService::GetSegmentInfoForText(const nsAString& aText,
|
||||||
nsIDOMMozSmsSegmentInfo** aResult)
|
nsIMobileMessageCallback* aRequest)
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(mRadioInterface, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(mRadioInterface, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
return mRadioInterface->GetSegmentInfoForText(aText, aResult);
|
return mRadioInterface->GetSegmentInfoForText(aText, aRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
@ -62,6 +62,11 @@ struct MarkMessageReadRequest
|
|||||||
bool value;
|
bool value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GetSegmentInfoForTextRequest
|
||||||
|
{
|
||||||
|
nsString text;
|
||||||
|
};
|
||||||
|
|
||||||
struct CreateThreadCursorRequest
|
struct CreateThreadCursorRequest
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
@ -73,6 +78,7 @@ union IPCSmsRequest
|
|||||||
GetMessageRequest;
|
GetMessageRequest;
|
||||||
DeleteMessageRequest;
|
DeleteMessageRequest;
|
||||||
MarkMessageReadRequest;
|
MarkMessageReadRequest;
|
||||||
|
GetSegmentInfoForTextRequest;
|
||||||
};
|
};
|
||||||
|
|
||||||
union IPCMobileMessageCursor
|
union IPCMobileMessageCursor
|
||||||
@ -122,9 +128,6 @@ parent:
|
|||||||
sync HasSupport()
|
sync HasSupport()
|
||||||
returns (bool aHasSupport);
|
returns (bool aHasSupport);
|
||||||
|
|
||||||
sync GetSegmentInfoForText(nsString aText)
|
|
||||||
returns (SmsSegmentInfoData aResult);
|
|
||||||
|
|
||||||
AddSilentNumber(nsString aNumber);
|
AddSilentNumber(nsString aNumber);
|
||||||
RemoveSilentNumber(nsString aNumber);
|
RemoveSilentNumber(nsString aNumber);
|
||||||
};
|
};
|
||||||
|
@ -66,6 +66,16 @@ struct ReplyMarkeMessageReadFail
|
|||||||
int32_t error;
|
int32_t error;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ReplyGetSegmentInfoForText
|
||||||
|
{
|
||||||
|
SmsSegmentInfoData infoData;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReplyGetSegmentInfoForTextFail
|
||||||
|
{
|
||||||
|
int32_t error;
|
||||||
|
};
|
||||||
|
|
||||||
union MessageReply
|
union MessageReply
|
||||||
{
|
{
|
||||||
ReplyMessageSend;
|
ReplyMessageSend;
|
||||||
@ -76,6 +86,8 @@ union MessageReply
|
|||||||
ReplyMessageDeleteFail;
|
ReplyMessageDeleteFail;
|
||||||
ReplyMarkeMessageRead;
|
ReplyMarkeMessageRead;
|
||||||
ReplyMarkeMessageReadFail;
|
ReplyMarkeMessageReadFail;
|
||||||
|
ReplyGetSegmentInfoForText;
|
||||||
|
ReplyGetSegmentInfoForTextFail;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mobilemessage
|
} // namespace mobilemessage
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "SmsChild.h"
|
#include "SmsChild.h"
|
||||||
#include "SmsMessage.h"
|
#include "SmsMessage.h"
|
||||||
#include "MmsMessage.h"
|
#include "MmsMessage.h"
|
||||||
|
#include "SmsSegmentInfo.h"
|
||||||
#include "Constants.h"
|
#include "Constants.h"
|
||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
#include "mozilla/Services.h"
|
#include "mozilla/Services.h"
|
||||||
@ -22,7 +23,7 @@ CreateMessageFromMessageData(const MobileMessageData& aData)
|
|||||||
{
|
{
|
||||||
nsCOMPtr<nsISupports> message;
|
nsCOMPtr<nsISupports> message;
|
||||||
|
|
||||||
switch(aData. type()) {
|
switch(aData.type()) {
|
||||||
case MobileMessageData::TMmsMessageData:
|
case MobileMessageData::TMmsMessageData:
|
||||||
message = new MmsMessage(aData.get_MmsMessageData());
|
message = new MmsMessage(aData.get_MmsMessageData());
|
||||||
break;
|
break;
|
||||||
@ -203,6 +204,17 @@ SmsRequestChild::Recv__delete__(const MessageReply& aReply)
|
|||||||
case MessageReply::TReplyMarkeMessageReadFail:
|
case MessageReply::TReplyMarkeMessageReadFail:
|
||||||
mReplyRequest->NotifyMarkMessageReadFailed(aReply.get_ReplyMarkeMessageReadFail().error());
|
mReplyRequest->NotifyMarkMessageReadFailed(aReply.get_ReplyMarkeMessageReadFail().error());
|
||||||
break;
|
break;
|
||||||
|
case MessageReply::TReplyGetSegmentInfoForText: {
|
||||||
|
const SmsSegmentInfoData& data =
|
||||||
|
aReply.get_ReplyGetSegmentInfoForText().infoData();
|
||||||
|
nsCOMPtr<nsIDOMMozSmsSegmentInfo> info = new SmsSegmentInfo(data);
|
||||||
|
mReplyRequest->NotifySegmentInfoForTextGot(info);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageReply::TReplyGetSegmentInfoForTextFail:
|
||||||
|
mReplyRequest->NotifyGetSegmentInfoForTextFailed(
|
||||||
|
aReply.get_ReplyGetSegmentInfoForTextFail().error());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
MOZ_CRASH("Received invalid response parameters!");
|
MOZ_CRASH("Received invalid response parameters!");
|
||||||
}
|
}
|
||||||
|
@ -96,19 +96,11 @@ SmsIPCService::HasSupport(bool* aHasSupport)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
SmsIPCService::GetSegmentInfoForText(const nsAString & aText,
|
SmsIPCService::GetSegmentInfoForText(const nsAString& aText,
|
||||||
nsIDOMMozSmsSegmentInfo** aResult)
|
nsIMobileMessageCallback* aRequest)
|
||||||
{
|
{
|
||||||
PSmsChild* smsChild = GetSmsChild();
|
return SendRequest(GetSegmentInfoForTextRequest(nsString(aText)),
|
||||||
NS_ENSURE_TRUE(smsChild, NS_ERROR_FAILURE);
|
aRequest);
|
||||||
|
|
||||||
SmsSegmentInfoData data;
|
|
||||||
bool ok = smsChild->SendGetSegmentInfoForText(nsString(aText), &data);
|
|
||||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMMozSmsSegmentInfo> info = new SmsSegmentInfo(data);
|
|
||||||
info.forget(aResult);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
@ -311,35 +311,6 @@ SmsParent::RecvHasSupport(bool* aHasSupport)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
SmsParent::RecvGetSegmentInfoForText(const nsString& aText,
|
|
||||||
SmsSegmentInfoData* aResult)
|
|
||||||
{
|
|
||||||
aResult->segments() = 0;
|
|
||||||
aResult->charsPerSegment() = 0;
|
|
||||||
aResult->charsAvailableInLastSegment() = 0;
|
|
||||||
|
|
||||||
nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
|
|
||||||
NS_ENSURE_TRUE(smsService, true);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMMozSmsSegmentInfo> info;
|
|
||||||
nsresult rv = smsService->GetSegmentInfoForText(aText, getter_AddRefs(info));
|
|
||||||
NS_ENSURE_SUCCESS(rv, true);
|
|
||||||
|
|
||||||
int segments, charsPerSegment, charsAvailableInLastSegment;
|
|
||||||
if (NS_FAILED(info->GetSegments(&segments)) ||
|
|
||||||
NS_FAILED(info->GetCharsPerSegment(&charsPerSegment)) ||
|
|
||||||
NS_FAILED(info->GetCharsAvailableInLastSegment(&charsAvailableInLastSegment))) {
|
|
||||||
NS_ERROR("Can't get attribute values from nsIDOMMozSmsSegmentInfo");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
aResult->segments() = segments;
|
|
||||||
aResult->charsPerSegment() = charsPerSegment;
|
|
||||||
aResult->charsAvailableInLastSegment() = charsAvailableInLastSegment;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SmsParent::RecvAddSilentNumber(const nsString& aNumber)
|
SmsParent::RecvAddSilentNumber(const nsString& aNumber)
|
||||||
{
|
{
|
||||||
@ -393,6 +364,8 @@ SmsParent::RecvPSmsRequestConstructor(PSmsRequestParent* aActor,
|
|||||||
return actor->DoRequest(aRequest.get_DeleteMessageRequest());
|
return actor->DoRequest(aRequest.get_DeleteMessageRequest());
|
||||||
case IPCSmsRequest::TMarkMessageReadRequest:
|
case IPCSmsRequest::TMarkMessageReadRequest:
|
||||||
return actor->DoRequest(aRequest.get_MarkMessageReadRequest());
|
return actor->DoRequest(aRequest.get_MarkMessageReadRequest());
|
||||||
|
case IPCSmsRequest::TGetSegmentInfoForTextRequest:
|
||||||
|
return actor->DoRequest(aRequest.get_GetSegmentInfoForTextRequest());
|
||||||
default:
|
default:
|
||||||
MOZ_CRASH("Unknown type!");
|
MOZ_CRASH("Unknown type!");
|
||||||
}
|
}
|
||||||
@ -577,6 +550,24 @@ SmsRequestParent::DoRequest(const MarkMessageReadRequest& aRequest)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
SmsRequestParent::DoRequest(const GetSegmentInfoForTextRequest& aRequest)
|
||||||
|
{
|
||||||
|
nsresult rv = NS_ERROR_FAILURE;
|
||||||
|
|
||||||
|
nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
|
||||||
|
if (smsService) {
|
||||||
|
rv = smsService->GetSegmentInfoForText(aRequest.text(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return NS_SUCCEEDED(NotifyGetSegmentInfoForTextFailed(
|
||||||
|
nsIMobileMessageCallback::INTERNAL_ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
SmsRequestParent::SendReply(const MessageReply& aReply)
|
SmsRequestParent::SendReply(const MessageReply& aReply)
|
||||||
{
|
{
|
||||||
@ -674,6 +665,19 @@ SmsRequestParent::NotifyMarkMessageReadFailed(int32_t aError)
|
|||||||
return SendReply(ReplyMarkeMessageReadFail(aError));
|
return SendReply(ReplyMarkeMessageReadFail(aError));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
SmsRequestParent::NotifySegmentInfoForTextGot(nsIDOMMozSmsSegmentInfo *aInfo)
|
||||||
|
{
|
||||||
|
SmsSegmentInfo* info = static_cast<SmsSegmentInfo*>(aInfo);
|
||||||
|
return SendReply(ReplyGetSegmentInfoForText(info->GetData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
SmsRequestParent::NotifyGetSegmentInfoForTextFailed(int32_t aError)
|
||||||
|
{
|
||||||
|
return SendReply(ReplyGetSegmentInfoForTextFail(aError));
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* MobileMessageCursorParent
|
* MobileMessageCursorParent
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
@ -34,9 +34,6 @@ protected:
|
|||||||
virtual bool
|
virtual bool
|
||||||
RecvHasSupport(bool* aHasSupport) MOZ_OVERRIDE;
|
RecvHasSupport(bool* aHasSupport) MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual bool
|
|
||||||
RecvGetSegmentInfoForText(const nsString& aText, SmsSegmentInfoData* aResult) MOZ_OVERRIDE;
|
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
RecvAddSilentNumber(const nsString& aNumber) MOZ_OVERRIDE;
|
RecvAddSilentNumber(const nsString& aNumber) MOZ_OVERRIDE;
|
||||||
|
|
||||||
@ -120,6 +117,9 @@ protected:
|
|||||||
bool
|
bool
|
||||||
DoRequest(const MarkMessageReadRequest& aRequest);
|
DoRequest(const MarkMessageReadRequest& aRequest);
|
||||||
|
|
||||||
|
bool
|
||||||
|
DoRequest(const GetSegmentInfoForTextRequest& aRequest);
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
SendReply(const MessageReply& aReply);
|
SendReply(const MessageReply& aReply);
|
||||||
};
|
};
|
||||||
|
@ -49,13 +49,31 @@ let tasks = {
|
|||||||
function addTest(text, segments, charsPerSegment, charsAvailableInLastSegment) {
|
function addTest(text, segments, charsPerSegment, charsAvailableInLastSegment) {
|
||||||
tasks.push(function () {
|
tasks.push(function () {
|
||||||
log("Testing '" + text + "' ...");
|
log("Testing '" + text + "' ...");
|
||||||
let info = manager.getSegmentInfoForText(text);
|
let domRequest = manager.getSegmentInfoForText(text);
|
||||||
is(info.segments, segments, "info.segments");
|
ok(domRequest, "DOMRequest object returned.");
|
||||||
is(info.charsPerSegment, charsPerSegment, "info.charsPerSegment");
|
|
||||||
is(info.charsAvailableInLastSegment, charsAvailableInLastSegment,
|
|
||||||
"info.charsAvailableInLastSegment");
|
|
||||||
|
|
||||||
tasks.next();
|
domRequest.onsuccess = function(e) {
|
||||||
|
log("Received 'onsuccess' DOMRequest event.");
|
||||||
|
|
||||||
|
let result = e.target.result;
|
||||||
|
if (!result) {
|
||||||
|
ok(false, "getSegmentInfoForText() result is not valid.");
|
||||||
|
tasks.finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
is(result.segments, segments, "info.segments");
|
||||||
|
is(result.charsPerSegment, charsPerSegment, "info.charsPerSegment");
|
||||||
|
is(result.charsAvailableInLastSegment, charsAvailableInLastSegment,
|
||||||
|
"info.charsAvailableInLastSegment");
|
||||||
|
|
||||||
|
tasks.next();
|
||||||
|
};
|
||||||
|
|
||||||
|
domRequest.onerror = function(e) {
|
||||||
|
ok(false, "Failed to call getSegmentInfoForText().");
|
||||||
|
tasks.finish();
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,9 +81,9 @@ function addTestThrows(text) {
|
|||||||
tasks.push(function () {
|
tasks.push(function () {
|
||||||
log("Testing '" + text + "' ...");
|
log("Testing '" + text + "' ...");
|
||||||
try {
|
try {
|
||||||
let info = manager.getSegmentInfoForText(text);
|
let domRequest = manager.getSegmentInfoForText(text);
|
||||||
|
|
||||||
ok(false, "Not thrown");
|
ok(false, "Not thrown.");
|
||||||
tasks.finish();
|
tasks.finish();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
tasks.next();
|
tasks.next();
|
||||||
|
@ -12,7 +12,8 @@ const LEN_UCS2_WITH_16BIT_REF = 66;
|
|||||||
|
|
||||||
SpecialPowers.setBoolPref("dom.sms.enabled", true);
|
SpecialPowers.setBoolPref("dom.sms.enabled", true);
|
||||||
let currentStrict7BitEncoding = false;
|
let currentStrict7BitEncoding = false;
|
||||||
SpecialPowers.setBoolPref("dom.sms.strict7BitEncoding", currentStrict7BitEncoding);
|
SpecialPowers.setBoolPref("dom.sms.strict7BitEncoding",
|
||||||
|
currentStrict7BitEncoding);
|
||||||
SpecialPowers.addPermission("sms", true, document);
|
SpecialPowers.addPermission("sms", true, document);
|
||||||
|
|
||||||
let manager = window.navigator.mozMobileMessage;
|
let manager = window.navigator.mozMobileMessage;
|
||||||
@ -23,107 +24,165 @@ function times(str, n) {
|
|||||||
return (new Array(n + 1)).join(str);
|
return (new Array(n + 1)).join(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function doTest(text, strict7BitEncoding, expected) {
|
let tasks = {
|
||||||
if (strict7BitEncoding != currentStrict7BitEncoding) {
|
// List of test fuctions. Each of them should call |tasks.next()| when
|
||||||
currentStrict7BitEncoding = strict7BitEncoding;
|
// completed or |tasks.finish()| to jump to the last one.
|
||||||
SpecialPowers.setBoolPref("dom.sms.strict7BitEncoding", currentStrict7BitEncoding);
|
_tasks: [],
|
||||||
|
_nextTaskIndex: 0,
|
||||||
|
|
||||||
|
push: function push(func) {
|
||||||
|
this._tasks.push(func);
|
||||||
|
},
|
||||||
|
|
||||||
|
next: function next() {
|
||||||
|
let index = this._nextTaskIndex++;
|
||||||
|
let task = this._tasks[index];
|
||||||
|
try {
|
||||||
|
task();
|
||||||
|
} catch (ex) {
|
||||||
|
ok(false, "test task[" + index + "] throws: " + ex);
|
||||||
|
// Run last task as clean up if possible.
|
||||||
|
if (index != this._tasks.length - 1) {
|
||||||
|
this.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
finish: function finish() {
|
||||||
|
this._tasks[this._tasks.length - 1]();
|
||||||
|
},
|
||||||
|
|
||||||
|
run: function run() {
|
||||||
|
this.next();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let result = manager.getSegmentInfoForText(text);
|
function addTest(text, strict7BitEncoding, expected) {
|
||||||
ok(result, "result of GetSegmentInfoForText is valid");
|
tasks.push(function () {
|
||||||
is(result.segments, expected[0], "segments");
|
if (strict7BitEncoding != currentStrict7BitEncoding) {
|
||||||
is(result.charsPerSegment, expected[1], "charsPerSegment");
|
currentStrict7BitEncoding = strict7BitEncoding;
|
||||||
is(result.charsAvailableInLastSegment, expected[2], "charsAvailableInLastSegment");
|
SpecialPowers.setBoolPref("dom.sms.strict7BitEncoding",
|
||||||
}
|
currentStrict7BitEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
function cleanUp() {
|
let domRequest = manager.getSegmentInfoForText(text);
|
||||||
SpecialPowers.removePermission("sms", document);
|
ok(domRequest, "DOMRequest object returned.");
|
||||||
SpecialPowers.clearUserPref("dom.sms.enabled");
|
|
||||||
SpecialPowers.clearUserPref("dom.sms.strict7BitEncoding");
|
domRequest.onsuccess = function(e) {
|
||||||
finish();
|
log("Received 'onsuccess' DOMRequest event.");
|
||||||
|
|
||||||
|
let result = e.target.result;
|
||||||
|
if (!result) {
|
||||||
|
ok(false, "getSegmentInfoForText() result is not valid.");
|
||||||
|
tasks.finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
is(result.segments, expected[0], "segments");
|
||||||
|
is(result.charsPerSegment, expected[1], "charsPerSegment");
|
||||||
|
is(result.charsAvailableInLastSegment, expected[2],
|
||||||
|
"charsAvailableInLastSegment");
|
||||||
|
|
||||||
|
tasks.next();
|
||||||
|
};
|
||||||
|
|
||||||
|
domRequest.onerror = function(e) {
|
||||||
|
ok(false, "Failed to call getSegmentInfoForText().");
|
||||||
|
tasks.finish();
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// GSM 7Bit Alphabets:
|
// GSM 7Bit Alphabets:
|
||||||
//
|
//
|
||||||
// 'a' is in GSM default locking shift table, so it takes 1 septet.
|
// 'a' is in GSM default locking shift table, so it takes 1 septet.
|
||||||
doTest("a", false, [1, LEN_7BIT, LEN_7BIT - 1]);
|
addTest("a", false, [1, LEN_7BIT, LEN_7BIT - 1]);
|
||||||
// '\u20ac' is in GSM default single shift table, so it takes 2 septets.
|
// '\u20ac' is in GSM default single shift table, so it takes 2 septets.
|
||||||
doTest("\u20ac", false, [1, LEN_7BIT, LEN_7BIT - 2]);
|
addTest("\u20ac", false, [1, LEN_7BIT, LEN_7BIT - 2]);
|
||||||
// SP is defined in both locking shift and single shift tables.
|
// SP is defined in both locking shift and single shift tables.
|
||||||
doTest(" ", false, [1, LEN_7BIT, LEN_7BIT - 1]);
|
addTest(" ", false, [1, LEN_7BIT, LEN_7BIT - 1]);
|
||||||
// Some combinations.
|
// Some combinations.
|
||||||
doTest("a\u20ac", false, [1, LEN_7BIT, LEN_7BIT - 3]);
|
addTest("a\u20ac", false, [1, LEN_7BIT, LEN_7BIT - 3]);
|
||||||
doTest("a ", false, [1, LEN_7BIT, LEN_7BIT - 2]);
|
addTest("a ", false, [1, LEN_7BIT, LEN_7BIT - 2]);
|
||||||
doTest("\u20aca", false, [1, LEN_7BIT, LEN_7BIT - 3]);
|
addTest("\u20aca", false, [1, LEN_7BIT, LEN_7BIT - 3]);
|
||||||
doTest("\u20ac ", false, [1, LEN_7BIT, LEN_7BIT - 3]);
|
addTest("\u20ac ", false, [1, LEN_7BIT, LEN_7BIT - 3]);
|
||||||
doTest(" \u20ac", false, [1, LEN_7BIT, LEN_7BIT - 3]);
|
addTest(" \u20ac", false, [1, LEN_7BIT, LEN_7BIT - 3]);
|
||||||
doTest(" a", false, [1, LEN_7BIT, LEN_7BIT - 2]);
|
addTest(" a", false, [1, LEN_7BIT, LEN_7BIT - 2]);
|
||||||
|
|
||||||
// GSM 7Bit Alphabets (multipart):
|
// GSM 7Bit Alphabets (multipart):
|
||||||
//
|
//
|
||||||
// Exactly 160 locking shift table chararacters.
|
// Exactly 160 locking shift table chararacters.
|
||||||
doTest(times("a", LEN_7BIT), false, [1, LEN_7BIT, 0]);
|
addTest(times("a", LEN_7BIT), false, [1, LEN_7BIT, 0]);
|
||||||
// 161 locking shift table chararacters. We'll have |161 - 153 = 8| septets in
|
// 161 locking shift table chararacters. We'll have |161 - 153 = 8| septets in
|
||||||
// the 2nd segment.
|
// the 2nd segment.
|
||||||
doTest(times("a", LEN_7BIT + 1), false,
|
addTest(times("a", LEN_7BIT + 1), false,
|
||||||
[2, LEN_7BIT_WITH_8BIT_REF, LEN_7BIT_WITH_8BIT_REF - 8]);
|
[2, LEN_7BIT_WITH_8BIT_REF, LEN_7BIT_WITH_8BIT_REF - 8]);
|
||||||
// |LEN_7BIT_WITH_8BIT_REF * 2| locking shift table chararacters.
|
// |LEN_7BIT_WITH_8BIT_REF * 2| locking shift table chararacters.
|
||||||
doTest(times("a", LEN_7BIT_WITH_8BIT_REF * 2), false,
|
addTest(times("a", LEN_7BIT_WITH_8BIT_REF * 2), false,
|
||||||
[2, LEN_7BIT_WITH_8BIT_REF, 0]);
|
[2, LEN_7BIT_WITH_8BIT_REF, 0]);
|
||||||
// |LEN_7BIT_WITH_8BIT_REF * 2 + 1| locking shift table chararacters.
|
// |LEN_7BIT_WITH_8BIT_REF * 2 + 1| locking shift table chararacters.
|
||||||
doTest(times("a", LEN_7BIT_WITH_8BIT_REF * 2 + 1), false,
|
addTest(times("a", LEN_7BIT_WITH_8BIT_REF * 2 + 1), false,
|
||||||
[3, LEN_7BIT_WITH_8BIT_REF, LEN_7BIT_WITH_8BIT_REF - 1]);
|
[3, LEN_7BIT_WITH_8BIT_REF, LEN_7BIT_WITH_8BIT_REF - 1]);
|
||||||
// Exactly 80 single shift table chararacters.
|
// Exactly 80 single shift table chararacters.
|
||||||
doTest(times("\u20ac", LEN_7BIT / 2), false, [1, LEN_7BIT, 0]);
|
addTest(times("\u20ac", LEN_7BIT / 2), false, [1, LEN_7BIT, 0]);
|
||||||
// 81 single shift table chararacters. Because |Math.floor(153 / 2) = 76|, it
|
// 81 single shift table chararacters. Because |Math.floor(153 / 2) = 76|, it
|
||||||
// should left 5 septets in the 2nd segment.
|
// should left 5 septets in the 2nd segment.
|
||||||
doTest(times("\u20ac", LEN_7BIT / 2 + 1), false,
|
addTest(times("\u20ac", LEN_7BIT / 2 + 1), false,
|
||||||
[2, LEN_7BIT_WITH_8BIT_REF, LEN_7BIT_WITH_8BIT_REF - 10]);
|
[2, LEN_7BIT_WITH_8BIT_REF, LEN_7BIT_WITH_8BIT_REF - 10]);
|
||||||
// |1 + 2 * 76| single shift table chararacters. We have only |153 - 76 * 2 = 1|
|
// |1 + 2 * 76| single shift table chararacters. We have only |153 - 76 * 2 = 1|
|
||||||
// space left, but each single shift table character takes 2, so it will be
|
// space left, but each single shift table character takes 2, so it will be
|
||||||
// filled in the 3rd segment.
|
// filled in the 3rd segment.
|
||||||
doTest(times("\u20ac", 1 + 2 * Math.floor(LEN_7BIT_WITH_8BIT_REF / 2)), false,
|
addTest(times("\u20ac", 1 + 2 * Math.floor(LEN_7BIT_WITH_8BIT_REF / 2)), false,
|
||||||
[3, LEN_7BIT_WITH_8BIT_REF, LEN_7BIT_WITH_8BIT_REF - 2]);
|
[3, LEN_7BIT_WITH_8BIT_REF, LEN_7BIT_WITH_8BIT_REF - 2]);
|
||||||
// |2 * 76| single shift table chararacters + 1 locking shift table chararacter.
|
// |2 * 76| single shift table chararacters + 1 locking shift table chararacter.
|
||||||
doTest("a" + times("\u20ac", 2 * Math.floor(LEN_7BIT_WITH_8BIT_REF / 2)), false,
|
addTest("a" + times("\u20ac", 2 * Math.floor(LEN_7BIT_WITH_8BIT_REF / 2)), false,
|
||||||
[2, LEN_7BIT_WITH_8BIT_REF, 1]);
|
[2, LEN_7BIT_WITH_8BIT_REF, 1]);
|
||||||
doTest(times("\u20ac", 2 * Math.floor(LEN_7BIT_WITH_8BIT_REF / 2)) + "a", false,
|
addTest(times("\u20ac", 2 * Math.floor(LEN_7BIT_WITH_8BIT_REF / 2)) + "a", false,
|
||||||
[2, LEN_7BIT_WITH_8BIT_REF, 0]);
|
[2, LEN_7BIT_WITH_8BIT_REF, 0]);
|
||||||
|
|
||||||
// UCS2:
|
// UCS2:
|
||||||
//
|
//
|
||||||
// '\u6afb' should be encoded as UCS2.
|
// '\u6afb' should be encoded as UCS2.
|
||||||
doTest("\u6afb", false, [1, LEN_UCS2, LEN_UCS2 - 1]);
|
addTest("\u6afb", false, [1, LEN_UCS2, LEN_UCS2 - 1]);
|
||||||
// Combination of GSM 7bit alphabets.
|
// Combination of GSM 7bit alphabets.
|
||||||
doTest("\u6afba", false, [1, LEN_UCS2, LEN_UCS2 - 2]);
|
addTest("\u6afba", false, [1, LEN_UCS2, LEN_UCS2 - 2]);
|
||||||
doTest("\u6afb\u20ac", false, [1, LEN_UCS2, LEN_UCS2 - 2]);
|
addTest("\u6afb\u20ac", false, [1, LEN_UCS2, LEN_UCS2 - 2]);
|
||||||
doTest("\u6afb ", false, [1, LEN_UCS2, LEN_UCS2 - 2]);
|
addTest("\u6afb ", false, [1, LEN_UCS2, LEN_UCS2 - 2]);
|
||||||
|
|
||||||
// UCS2 (multipart):
|
// UCS2 (multipart):
|
||||||
//
|
//
|
||||||
// Exactly 70 UCS2 chararacters.
|
// Exactly 70 UCS2 chararacters.
|
||||||
doTest(times("\u6afb", LEN_UCS2), false, [1, LEN_UCS2, 0]);
|
addTest(times("\u6afb", LEN_UCS2), false, [1, LEN_UCS2, 0]);
|
||||||
// 71 UCS2 chararacters. We'll have |71 - 67 = 4| chararacters in the 2nd
|
// 71 UCS2 chararacters. We'll have |71 - 67 = 4| chararacters in the 2nd
|
||||||
// segment.
|
// segment.
|
||||||
doTest(times("\u6afb", LEN_UCS2 + 1), false,
|
addTest(times("\u6afb", LEN_UCS2 + 1), false,
|
||||||
[2, LEN_UCS2_WITH_8BIT_REF, LEN_UCS2_WITH_8BIT_REF - 4]);
|
[2, LEN_UCS2_WITH_8BIT_REF, LEN_UCS2_WITH_8BIT_REF - 4]);
|
||||||
// |LEN_UCS2_WITH_8BIT_REF * 2| ucs2 chararacters.
|
// |LEN_UCS2_WITH_8BIT_REF * 2| ucs2 chararacters.
|
||||||
doTest(times("\u6afb", LEN_UCS2_WITH_8BIT_REF * 2), false,
|
addTest(times("\u6afb", LEN_UCS2_WITH_8BIT_REF * 2), false,
|
||||||
[2, LEN_UCS2_WITH_8BIT_REF, 0]);
|
[2, LEN_UCS2_WITH_8BIT_REF, 0]);
|
||||||
// |LEN_7BIT_WITH_8BIT_REF * 2 + 1| ucs2 chararacters.
|
// |LEN_7BIT_WITH_8BIT_REF * 2 + 1| ucs2 chararacters.
|
||||||
doTest(times("\u6afb", LEN_UCS2_WITH_8BIT_REF * 2 + 1), false,
|
addTest(times("\u6afb", LEN_UCS2_WITH_8BIT_REF * 2 + 1), false,
|
||||||
[3, LEN_UCS2_WITH_8BIT_REF, LEN_UCS2_WITH_8BIT_REF - 1]);
|
[3, LEN_UCS2_WITH_8BIT_REF, LEN_UCS2_WITH_8BIT_REF - 1]);
|
||||||
|
|
||||||
// Strict 7-Bit Encoding:
|
// Strict 7-Bit Encoding:
|
||||||
//
|
//
|
||||||
// Should have no effect on GSM default alphabet characters.
|
// Should have no effect on GSM default alphabet characters.
|
||||||
doTest("\u0041", true, [1, LEN_7BIT, LEN_7BIT - 1]);
|
addTest("\u0041", true, [1, LEN_7BIT, LEN_7BIT - 1]);
|
||||||
// "\u00c0"(À) should be mapped to "\u0041"(A).
|
// "\u00c0"(À) should be mapped to "\u0041"(A).
|
||||||
doTest("\u00c0", true, [1, LEN_7BIT, LEN_7BIT - 1]);
|
addTest("\u00c0", true, [1, LEN_7BIT, LEN_7BIT - 1]);
|
||||||
// Mixing mapped characters with unmapped ones.
|
// Mixing mapped characters with unmapped ones.
|
||||||
doTest("\u00c0\u0041", true, [1, LEN_7BIT, LEN_7BIT - 2]);
|
addTest("\u00c0\u0041", true, [1, LEN_7BIT, LEN_7BIT - 2]);
|
||||||
doTest("\u0041\u00c0", true, [1, LEN_7BIT, LEN_7BIT - 2]);
|
addTest("\u0041\u00c0", true, [1, LEN_7BIT, LEN_7BIT - 2]);
|
||||||
// UCS2 characters should be mapped to '*'.
|
// UCS2 characters should be mapped to '*'.
|
||||||
doTest("\u1234", true, [1, LEN_7BIT, LEN_7BIT - 1]);
|
addTest("\u1234", true, [1, LEN_7BIT, LEN_7BIT - 1]);
|
||||||
|
|
||||||
cleanUp();
|
|
||||||
|
// WARNING: All tasks should be pushed before this!!!
|
||||||
|
tasks.push(function cleanUp() {
|
||||||
|
SpecialPowers.removePermission("sms", document);
|
||||||
|
SpecialPowers.clearUserPref("dom.sms.enabled");
|
||||||
|
SpecialPowers.clearUserPref("dom.sms.strict7BitEncoding");
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
tasks.run();
|
||||||
|
@ -209,7 +209,7 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget
|
|||||||
*
|
*
|
||||||
* Otherwise, the request's onerror will be called, and the request's error
|
* Otherwise, the request's onerror will be called, and the request's error
|
||||||
* will be either 'RadioNotAvailable', 'RequestNotSupported',
|
* will be either 'RadioNotAvailable', 'RequestNotSupported',
|
||||||
* 'IllegalSIMorME', or 'GenericFailure'
|
* 'IllegalSIMorME', 'InvalidParameter', or 'GenericFailure'
|
||||||
*/
|
*/
|
||||||
nsIDOMDOMRequest setCallForwardingOption(in nsIDOMMozMobileCFInfo CFInfo);
|
nsIDOMDOMRequest setCallForwardingOption(in nsIDOMMozMobileCFInfo CFInfo);
|
||||||
|
|
||||||
@ -226,7 +226,7 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget
|
|||||||
*
|
*
|
||||||
* Otherwise, the request's onerror will be called, and the request's error
|
* Otherwise, the request's onerror will be called, and the request's error
|
||||||
* will be either 'RadioNotAvailable', 'RequestNotSupported',
|
* will be either 'RadioNotAvailable', 'RequestNotSupported',
|
||||||
* or 'GenericFailure'.
|
* 'InvalidParameter', or 'GenericFailure'.
|
||||||
*/
|
*/
|
||||||
nsIDOMDOMRequest getCallForwardingOption(in unsigned short reason);
|
nsIDOMDOMRequest getCallForwardingOption(in unsigned short reason);
|
||||||
|
|
||||||
@ -241,7 +241,7 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget
|
|||||||
*
|
*
|
||||||
* Otherwise, the request's onerror will be called, and the request's error
|
* Otherwise, the request's onerror will be called, and the request's error
|
||||||
* will be either 'RadioNotAvailable', 'RequestNotSupported',
|
* will be either 'RadioNotAvailable', 'RequestNotSupported',
|
||||||
* 'IllegalSIMorME', 'InvalidCallBarringOption' or 'GenericFailure'
|
* 'IllegalSIMorME', 'InvalidParameter', or 'GenericFailure'
|
||||||
*/
|
*/
|
||||||
nsIDOMDOMRequest setCallBarringOption(in jsval option);
|
nsIDOMDOMRequest setCallBarringOption(in jsval option);
|
||||||
|
|
||||||
@ -259,7 +259,7 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget
|
|||||||
*
|
*
|
||||||
* Otherwise, the request's onerror will be called, and the request's error
|
* Otherwise, the request's onerror will be called, and the request's error
|
||||||
* will be either 'RadioNotAvailable', 'RequestNotSupported',
|
* will be either 'RadioNotAvailable', 'RequestNotSupported',
|
||||||
* 'InvalidCallBarringOption' or 'GenericFailure'.
|
* 'InvalidParameter', or 'GenericFailure'.
|
||||||
*/
|
*/
|
||||||
nsIDOMDOMRequest getCallBarringOption(in jsval option);
|
nsIDOMDOMRequest getCallBarringOption(in jsval option);
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ function testSetCallBarringOptionError(option) {
|
|||||||
+ JSON.stringify(option));
|
+ JSON.stringify(option));
|
||||||
};
|
};
|
||||||
request.onerror = function(event) {
|
request.onerror = function(event) {
|
||||||
is(event.target.error.name, 'InvalidCallBarringOption', JSON.stringify(option));
|
is(event.target.error.name, 'InvalidParameter', JSON.stringify(option));
|
||||||
nextTest();
|
nextTest();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ this.PhoneNumberUtils = {
|
|||||||
|
|
||||||
// Get SIM mcc
|
// Get SIM mcc
|
||||||
let iccInfo = mobileConnection.iccInfo;
|
let iccInfo = mobileConnection.iccInfo;
|
||||||
if (!mcc && iccInfo.mcc) {
|
if (!mcc && iccInfo && iccInfo.mcc) {
|
||||||
mcc = iccInfo.mcc;
|
mcc = iccInfo.mcc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,8 +426,11 @@ GonkGPSGeolocationProvider::RequestSetID(uint32_t flags)
|
|||||||
nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
|
nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
|
||||||
rilCtx->GetIccInfo(getter_AddRefs(iccInfo));
|
rilCtx->GetIccInfo(getter_AddRefs(iccInfo));
|
||||||
if (iccInfo) {
|
if (iccInfo) {
|
||||||
type = AGPS_SETID_TYPE_MSISDN;
|
nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
|
||||||
iccInfo->GetMsisdn(id);
|
if (gsmIccInfo) {
|
||||||
|
type = AGPS_SETID_TYPE_MSISDN;
|
||||||
|
gsmIccInfo->GetMsisdn(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +45,10 @@ if (DEBUG) {
|
|||||||
|
|
||||||
const RILCONTENTHELPER_CID =
|
const RILCONTENTHELPER_CID =
|
||||||
Components.ID("{472816e1-1fd6-4405-996c-806f9ea68174}");
|
Components.ID("{472816e1-1fd6-4405-996c-806f9ea68174}");
|
||||||
const ICCINFO_CID =
|
const GSMICCINFO_CID =
|
||||||
Components.ID("{fab2c0f0-d73a-11e2-8b8b-0800200c9a66}");
|
Components.ID("{e0fa785b-ad3f-46ed-bc56-fcb0d6fe4fa8}");
|
||||||
|
const CDMAICCINFO_CID =
|
||||||
|
Components.ID("{3d1f844f-9ec5-48fb-8907-aed2e5421709}");
|
||||||
const MOBILECONNECTIONINFO_CID =
|
const MOBILECONNECTIONINFO_CID =
|
||||||
Components.ID("{a35cfd39-2d93-4489-ac7d-396475dacb27}");
|
Components.ID("{a35cfd39-2d93-4489-ac7d-396475dacb27}");
|
||||||
const MOBILENETWORKINFO_CID =
|
const MOBILENETWORKINFO_CID =
|
||||||
@ -148,24 +150,50 @@ MobileIccCardLockRetryCount.prototype = {
|
|||||||
|
|
||||||
function IccInfo() {}
|
function IccInfo() {}
|
||||||
IccInfo.prototype = {
|
IccInfo.prototype = {
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozIccInfo]),
|
iccType: null,
|
||||||
classID: ICCINFO_CID,
|
|
||||||
classInfo: XPCOMUtils.generateCI({
|
|
||||||
classID: ICCINFO_CID,
|
|
||||||
classDescription: "IccInfo",
|
|
||||||
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
|
||||||
interfaces: [Ci.nsIDOMMozIccInfo]
|
|
||||||
}),
|
|
||||||
|
|
||||||
// nsIDOMMozIccInfo
|
|
||||||
|
|
||||||
iccid: null,
|
iccid: null,
|
||||||
mcc: null,
|
mcc: null,
|
||||||
mnc: null,
|
mnc: null,
|
||||||
spn: null,
|
spn: null,
|
||||||
|
isDisplayNetworkNameRequired: null,
|
||||||
|
isDisplaySpnRequired: null
|
||||||
|
};
|
||||||
|
|
||||||
|
function GsmIccInfo() {}
|
||||||
|
GsmIccInfo.prototype = {
|
||||||
|
__proto__: IccInfo.prototype,
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozGsmIccInfo]),
|
||||||
|
classID: GSMICCINFO_CID,
|
||||||
|
classInfo: XPCOMUtils.generateCI({
|
||||||
|
classID: GSMICCINFO_CID,
|
||||||
|
classDescription: "MozGsmIccInfo",
|
||||||
|
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
||||||
|
interfaces: [Ci.nsIDOMMozGsmIccInfo]
|
||||||
|
}),
|
||||||
|
|
||||||
|
// nsIDOMMozGsmIccInfo
|
||||||
|
|
||||||
msisdn: null
|
msisdn: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function CdmaIccInfo() {}
|
||||||
|
CdmaIccInfo.prototype = {
|
||||||
|
__proto__: IccInfo.prototype,
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozCdmaIccInfo]),
|
||||||
|
classID: CDMAICCINFO_CID,
|
||||||
|
classInfo: XPCOMUtils.generateCI({
|
||||||
|
classID: CDMAICCINFO_CID,
|
||||||
|
classDescription: "MozCdmaIccInfo",
|
||||||
|
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
||||||
|
interfaces: [Ci.nsIDOMMozCdmaIccInfo]
|
||||||
|
}),
|
||||||
|
|
||||||
|
// nsIDOMMozCdmaIccInfo
|
||||||
|
|
||||||
|
mdn: null,
|
||||||
|
min: null
|
||||||
|
};
|
||||||
|
|
||||||
function VoicemailInfo() {}
|
function VoicemailInfo() {}
|
||||||
VoicemailInfo.prototype = {
|
VoicemailInfo.prototype = {
|
||||||
number: null,
|
number: null,
|
||||||
@ -404,7 +432,7 @@ function RILContentHelper() {
|
|||||||
this.rilContext = {
|
this.rilContext = {
|
||||||
cardState: RIL.GECKO_CARDSTATE_UNKNOWN,
|
cardState: RIL.GECKO_CARDSTATE_UNKNOWN,
|
||||||
networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
|
networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
|
||||||
iccInfo: new IccInfo(),
|
iccInfo: null,
|
||||||
voiceConnectionInfo: new MobileConnectionInfo(),
|
voiceConnectionInfo: new MobileConnectionInfo(),
|
||||||
dataConnectionInfo: new MobileConnectionInfo()
|
dataConnectionInfo: new MobileConnectionInfo()
|
||||||
};
|
};
|
||||||
@ -472,7 +500,31 @@ RILContentHelper.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.updateInfo(srcNetwork, network);
|
this.updateInfo(srcNetwork, network);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need to consider below cases when update iccInfo:
|
||||||
|
* 1. Should clear iccInfo to null if there is no card detected.
|
||||||
|
* 2. Need to create corresponding object based on iccType.
|
||||||
|
*/
|
||||||
|
updateIccInfo: function updateIccInfo(newInfo) {
|
||||||
|
// Card is not detected, clear iccInfo to null.
|
||||||
|
if (!newInfo || !newInfo.iccType) {
|
||||||
|
this.rilContext.iccInfo = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If iccInfo is null, new corresponding object based on iccType.
|
||||||
|
if (!this.rilContext.iccInfo) {
|
||||||
|
if (newInfo.iccType === "ruim" || newInfo.iccType === "csim") {
|
||||||
|
this.rilContext.iccInfo = new CdmaIccInfo();
|
||||||
|
} else {
|
||||||
|
this.rilContext.iccInfo = new GsmIccInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateInfo(newInfo, this.rilContext.iccInfo);
|
||||||
|
},
|
||||||
|
|
||||||
_windowsMap: null,
|
_windowsMap: null,
|
||||||
|
|
||||||
@ -494,7 +546,7 @@ RILContentHelper.prototype = {
|
|||||||
}
|
}
|
||||||
this.rilContext.cardState = rilContext.cardState;
|
this.rilContext.cardState = rilContext.cardState;
|
||||||
this.rilContext.networkSelectionMode = rilContext.networkSelectionMode;
|
this.rilContext.networkSelectionMode = rilContext.networkSelectionMode;
|
||||||
this.updateInfo(rilContext.iccInfo, this.rilContext.iccInfo);
|
this.updateIccInfo(rilContext.iccInfo);
|
||||||
this.updateConnectionInfo(rilContext.voice, this.rilContext.voiceConnectionInfo);
|
this.updateConnectionInfo(rilContext.voice, this.rilContext.voiceConnectionInfo);
|
||||||
this.updateConnectionInfo(rilContext.data, this.rilContext.dataConnectionInfo);
|
this.updateConnectionInfo(rilContext.data, this.rilContext.dataConnectionInfo);
|
||||||
|
|
||||||
@ -643,7 +695,8 @@ RILContentHelper.prototype = {
|
|||||||
let requestId = this.getRequestId(request);
|
let requestId = this.getRequestId(request);
|
||||||
|
|
||||||
if (!mode) {
|
if (!mode) {
|
||||||
this.dispatchFireRequestError(requestId, "InvalidParameter");
|
this.dispatchFireRequestError(requestId,
|
||||||
|
RIL.GECKO_ERROR_INVALID_PARAMETER);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1006,7 +1059,8 @@ RILContentHelper.prototype = {
|
|||||||
let requestId = this.getRequestId(request);
|
let requestId = this.getRequestId(request);
|
||||||
|
|
||||||
if (!this._isValidCFReason(reason)){
|
if (!this._isValidCFReason(reason)){
|
||||||
this.dispatchFireRequestError(requestId, "Invalid call forwarding reason.");
|
this.dispatchFireRequestError(requestId,
|
||||||
|
RIL.GECKO_ERROR_INVALID_PARAMETER);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1032,7 +1086,8 @@ RILContentHelper.prototype = {
|
|||||||
if (!cfInfo ||
|
if (!cfInfo ||
|
||||||
!this._isValidCFReason(cfInfo.reason) ||
|
!this._isValidCFReason(cfInfo.reason) ||
|
||||||
!this._isValidCFAction(cfInfo.action)){
|
!this._isValidCFAction(cfInfo.action)){
|
||||||
this.dispatchFireRequestError(requestId, "Invalid call forwarding rule definition.");
|
this.dispatchFireRequestError(requestId,
|
||||||
|
RIL.GECKO_ERROR_INVALID_PARAMETER);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1061,7 +1116,8 @@ RILContentHelper.prototype = {
|
|||||||
|
|
||||||
if (DEBUG) debug("getCallBarringOption: " + JSON.stringify(option));
|
if (DEBUG) debug("getCallBarringOption: " + JSON.stringify(option));
|
||||||
if (!this._isValidCallBarringOption(option)) {
|
if (!this._isValidCallBarringOption(option)) {
|
||||||
this.dispatchFireRequestError(requestId, "InvalidCallBarringOption");
|
this.dispatchFireRequestError(requestId,
|
||||||
|
RIL.GECKO_ERROR_INVALID_PARAMETER);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1087,7 +1143,8 @@ RILContentHelper.prototype = {
|
|||||||
|
|
||||||
if (DEBUG) debug("setCallBarringOption: " + JSON.stringify(option));
|
if (DEBUG) debug("setCallBarringOption: " + JSON.stringify(option));
|
||||||
if (!this._isValidCallBarringOption(option, true)) {
|
if (!this._isValidCallBarringOption(option, true)) {
|
||||||
this.dispatchFireRequestError(requestId, "InvalidCallBarringOption");
|
this.dispatchFireRequestError(requestId,
|
||||||
|
RIL.GECKO_ERROR_INVALID_PARAMETER);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1541,7 +1598,7 @@ RILContentHelper.prototype = {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "RIL:IccInfoChanged":
|
case "RIL:IccInfoChanged":
|
||||||
this.updateInfo(msg.json.data, this.rilContext.iccInfo);
|
this.updateIccInfo(msg.json.data);
|
||||||
this._deliverEvent("_iccListeners", "notifyIccInfoChanged", null);
|
this._deliverEvent("_iccListeners", "notifyIccInfoChanged", null);
|
||||||
break;
|
break;
|
||||||
case "RIL:VoiceInfoChanged":
|
case "RIL:VoiceInfoChanged":
|
||||||
|
@ -2222,21 +2222,14 @@ RadioInterface.prototype = {
|
|||||||
let oldIccInfo = this.rilContext.iccInfo;
|
let oldIccInfo = this.rilContext.iccInfo;
|
||||||
this.rilContext.iccInfo = message;
|
this.rilContext.iccInfo = message;
|
||||||
|
|
||||||
let iccInfoChanged = !oldIccInfo ||
|
if (!this.isInfoChanged(message, oldIccInfo)) {
|
||||||
oldIccInfo.iccid != message.iccid ||
|
|
||||||
oldIccInfo.mcc != message.mcc ||
|
|
||||||
oldIccInfo.mnc != message.mnc ||
|
|
||||||
oldIccInfo.spn != message.spn ||
|
|
||||||
oldIccInfo.isDisplayNetworkNameRequired != message.isDisplayNetworkNameRequired ||
|
|
||||||
oldIccInfo.isDisplaySpnRequired != message.isDisplaySpnRequired ||
|
|
||||||
oldIccInfo.msisdn != message.msisdn;
|
|
||||||
if (!iccInfoChanged) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// RIL:IccInfoChanged corresponds to a DOM event that gets fired only
|
// RIL:IccInfoChanged corresponds to a DOM event that gets fired only
|
||||||
// when the MCC or MNC codes have changed.
|
// when iccInfo has changed.
|
||||||
gMessageManager.sendIccMessage("RIL:IccInfoChanged",
|
gMessageManager.sendIccMessage("RIL:IccInfoChanged",
|
||||||
this.clientId, message);
|
this.clientId,
|
||||||
|
message.iccType ? message : null);
|
||||||
|
|
||||||
// Update lastKnownSimMcc.
|
// Update lastKnownSimMcc.
|
||||||
if (message.mcc) {
|
if (message.mcc) {
|
||||||
@ -2994,7 +2987,7 @@ RadioInterface.prototype = {
|
|||||||
return options;
|
return options;
|
||||||
},
|
},
|
||||||
|
|
||||||
getSegmentInfoForText: function getSegmentInfoForText(text) {
|
getSegmentInfoForText: function getSegmentInfoForText(text, request) {
|
||||||
let strict7BitEncoding;
|
let strict7BitEncoding;
|
||||||
try {
|
try {
|
||||||
strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding");
|
strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding");
|
||||||
@ -3015,10 +3008,11 @@ RadioInterface.prototype = {
|
|||||||
charsInLastSegment = 0;
|
charsInLastSegment = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = gMobileMessageService.createSmsSegmentInfo(options.segmentMaxSeq,
|
let result = gMobileMessageService
|
||||||
options.segmentChars,
|
.createSmsSegmentInfo(options.segmentMaxSeq,
|
||||||
options.segmentChars - charsInLastSegment);
|
options.segmentChars,
|
||||||
return result;
|
options.segmentChars - charsInLastSegment);
|
||||||
|
request.notifySegmentInfoForTextGot(result);
|
||||||
},
|
},
|
||||||
|
|
||||||
sendSMS: function sendSMS(number, message, silent, request) {
|
sendSMS: function sendSMS(number, message, silent, request) {
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
interface nsIDOMMozIccInfo;
|
interface nsIDOMMozIccInfo;
|
||||||
interface nsIDOMMozMobileConnectionInfo;
|
interface nsIDOMMozMobileConnectionInfo;
|
||||||
interface nsIDOMMozSmsSegmentInfo;
|
|
||||||
interface nsIMobileMessageCallback;
|
interface nsIMobileMessageCallback;
|
||||||
|
|
||||||
[scriptable, uuid(1e602d20-d066-4399-8997-daf36b3158ef)]
|
[scriptable, uuid(1e602d20-d066-4399-8997-daf36b3158ef)]
|
||||||
@ -79,7 +78,7 @@ interface nsIRilContext : nsISupports
|
|||||||
readonly attribute nsIDOMMozMobileConnectionInfo data;
|
readonly attribute nsIDOMMozMobileConnectionInfo data;
|
||||||
};
|
};
|
||||||
|
|
||||||
[scriptable, uuid(5efcd358-080e-46d6-a7f7-4f36c204eec3)]
|
[scriptable, uuid(a50d65aa-00da-11e3-b954-7bfb233d98fc)]
|
||||||
interface nsIRadioInterface : nsISupports
|
interface nsIRadioInterface : nsISupports
|
||||||
{
|
{
|
||||||
readonly attribute nsIRilContext rilContext;
|
readonly attribute nsIRilContext rilContext;
|
||||||
@ -99,7 +98,8 @@ interface nsIRadioInterface : nsISupports
|
|||||||
/**
|
/**
|
||||||
* SMS-related functionality.
|
* SMS-related functionality.
|
||||||
*/
|
*/
|
||||||
nsIDOMMozSmsSegmentInfo getSegmentInfoForText(in DOMString text);
|
void getSegmentInfoForText(in DOMString text,
|
||||||
|
in nsIMobileMessageCallback request);
|
||||||
|
|
||||||
void sendSMS(in DOMString number,
|
void sendSMS(in DOMString number,
|
||||||
in DOMString message,
|
in DOMString message,
|
||||||
|
@ -370,6 +370,15 @@ this.CARD_APPTYPE_ISIM = 5;
|
|||||||
|
|
||||||
this.CARD_MAX_APPS = 8;
|
this.CARD_MAX_APPS = 8;
|
||||||
|
|
||||||
|
this.GECKO_CARD_TYPE = [
|
||||||
|
null,
|
||||||
|
"sim",
|
||||||
|
"usim",
|
||||||
|
"ruim",
|
||||||
|
"csim",
|
||||||
|
"isim"
|
||||||
|
];
|
||||||
|
|
||||||
this.NETWORK_STATE_UNKNOWN = "unknown";
|
this.NETWORK_STATE_UNKNOWN = "unknown";
|
||||||
this.NETWORK_STATE_AVAILABLE = "available";
|
this.NETWORK_STATE_AVAILABLE = "available";
|
||||||
this.NETWORK_STATE_CONNECTED = "connected";
|
this.NETWORK_STATE_CONNECTED = "connected";
|
||||||
|
@ -96,7 +96,99 @@ let RILQUIRKS_HAVE_QUERY_ICC_LOCK_RETRY_COUNT = libcutils.property_get("ro.moz.r
|
|||||||
// Marker object.
|
// Marker object.
|
||||||
let PENDING_NETWORK_TYPE = {};
|
let PENDING_NETWORK_TYPE = {};
|
||||||
|
|
||||||
let Buf = require("resource://gre/modules/workers/worker_buf.js");
|
let Buf = {
|
||||||
|
__proto__: (function(){
|
||||||
|
return require("resource://gre/modules/workers/worker_buf.js").Buf;
|
||||||
|
})(),
|
||||||
|
|
||||||
|
mToken: 0,
|
||||||
|
mTokenRequestMap: null,
|
||||||
|
|
||||||
|
init: function init() {
|
||||||
|
this._init();
|
||||||
|
|
||||||
|
// This gets incremented each time we send out a parcel.
|
||||||
|
this.mToken = 1;
|
||||||
|
|
||||||
|
// Maps tokens we send out with requests to the request type, so that
|
||||||
|
// when we get a response parcel back, we know what request it was for.
|
||||||
|
this.mTokenRequestMap = {};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process one parcel.
|
||||||
|
*/
|
||||||
|
processParcel: function processParcel() {
|
||||||
|
let response_type = this.readUint32();
|
||||||
|
|
||||||
|
let request_type, options;
|
||||||
|
if (response_type == RESPONSE_TYPE_SOLICITED) {
|
||||||
|
let token = this.readUint32();
|
||||||
|
let error = this.readUint32();
|
||||||
|
|
||||||
|
options = this.mTokenRequestMap[token];
|
||||||
|
if (!options) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Suspicious uninvited request found: " + token + ". Ignored!");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this.mTokenRequestMap[token];
|
||||||
|
request_type = options.rilRequestType;
|
||||||
|
|
||||||
|
options.rilRequestError = error;
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Solicited response for request type " + request_type +
|
||||||
|
", token " + token + ", error " + error);
|
||||||
|
}
|
||||||
|
} else if (response_type == RESPONSE_TYPE_UNSOLICITED) {
|
||||||
|
request_type = this.readUint32();
|
||||||
|
if (DEBUG) debug("Unsolicited response for request type " + request_type);
|
||||||
|
} else {
|
||||||
|
if (DEBUG) debug("Unknown response type: " + response_type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RIL.handleParcel(request_type, this.mReadAvailable, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new outgoing parcel.
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* Integer specifying the request type.
|
||||||
|
* @param options [optional]
|
||||||
|
* Object containing information about the request, e.g. the
|
||||||
|
* original main thread message object that led to the RIL request.
|
||||||
|
*/
|
||||||
|
newParcel: function newParcel(type, options) {
|
||||||
|
if (DEBUG) debug("New outgoing parcel of type " + type);
|
||||||
|
|
||||||
|
// We're going to leave room for the parcel size at the beginning.
|
||||||
|
this.mOutgoingIndex = this.PARCEL_SIZE_SIZE;
|
||||||
|
this.writeUint32(type);
|
||||||
|
this.writeUint32(this.mToken);
|
||||||
|
|
||||||
|
if (!options) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
options.rilRequestType = type;
|
||||||
|
options.rilRequestError = null;
|
||||||
|
this.mTokenRequestMap[this.mToken] = options;
|
||||||
|
this.mToken++;
|
||||||
|
return this.mToken;
|
||||||
|
},
|
||||||
|
|
||||||
|
simpleRequest: function simpleRequest(type, options) {
|
||||||
|
this.newParcel(type, options);
|
||||||
|
this.sendParcel();
|
||||||
|
},
|
||||||
|
|
||||||
|
onSendParcel: function onSendParcel(parcel) {
|
||||||
|
postRILMessage(CLIENT_ID, parcel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The RIL state machine.
|
* The RIL state machine.
|
||||||
@ -1222,6 +1314,10 @@ let RIL = {
|
|||||||
Buf.simpleRequest(REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, options);
|
Buf.simpleRequest(REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getCdmaSubscription: function getCdmaSubscription() {
|
||||||
|
Buf.simpleRequest(REQUEST_CDMA_SUBSCRIPTION);
|
||||||
|
},
|
||||||
|
|
||||||
exitEmergencyCbMode: function exitEmergencyCbMode(options) {
|
exitEmergencyCbMode: function exitEmergencyCbMode(options) {
|
||||||
// The function could be called by an API from RadioInterfaceLayer or by
|
// The function could be called by an API from RadioInterfaceLayer or by
|
||||||
// ril_worker itself. From ril_worker, we won't pass the parameter
|
// ril_worker itself. From ril_worker, we won't pass the parameter
|
||||||
@ -2837,6 +2933,9 @@ let RIL = {
|
|||||||
if (newCardState == this.cardState) {
|
if (newCardState == this.cardState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.iccInfo = {iccType: null};
|
||||||
|
ICCUtilsHelper.handleICCInfoChange();
|
||||||
|
|
||||||
this.cardState = newCardState;
|
this.cardState = newCardState;
|
||||||
this.sendChromeMessage({rilMessageType: "cardstatechange",
|
this.sendChromeMessage({rilMessageType: "cardstatechange",
|
||||||
cardState: this.cardState});
|
cardState: this.cardState});
|
||||||
@ -2888,6 +2987,8 @@ let RIL = {
|
|||||||
// This was moved down from CARD_APPSTATE_READY
|
// This was moved down from CARD_APPSTATE_READY
|
||||||
this.requestNetworkInfo();
|
this.requestNetworkInfo();
|
||||||
if (newCardState == GECKO_CARDSTATE_READY) {
|
if (newCardState == GECKO_CARDSTATE_READY) {
|
||||||
|
this.iccInfo.iccType = GECKO_CARD_TYPE[this.appType];
|
||||||
|
|
||||||
// For type SIM, we need to check EF_phase first.
|
// For type SIM, we need to check EF_phase first.
|
||||||
// Other types of ICC we can send Terminal_Profile immediately.
|
// Other types of ICC we can send Terminal_Profile immediately.
|
||||||
if (this.appType == CARD_APPTYPE_SIM) {
|
if (this.appType == CARD_APPTYPE_SIM) {
|
||||||
@ -5671,7 +5772,21 @@ RIL[REQUEST_GSM_SMS_BROADCAST_ACTIVATION] = null;
|
|||||||
RIL[REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG] = null;
|
RIL[REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG] = null;
|
||||||
RIL[REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG] = null;
|
RIL[REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG] = null;
|
||||||
RIL[REQUEST_CDMA_SMS_BROADCAST_ACTIVATION] = null;
|
RIL[REQUEST_CDMA_SMS_BROADCAST_ACTIVATION] = null;
|
||||||
RIL[REQUEST_CDMA_SUBSCRIPTION] = null;
|
RIL[REQUEST_CDMA_SUBSCRIPTION] = function REQUEST_CDMA_SUBSCRIPTION(length, options) {
|
||||||
|
if (options.rilRequestError) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = Buf.readStringList();
|
||||||
|
|
||||||
|
this.iccInfo.mdn = result[0];
|
||||||
|
// The result[1] is Home SID. (Already be handled in readCDMAHome())
|
||||||
|
// The result[2] is Home NID. (Already be handled in readCDMAHome())
|
||||||
|
this.iccInfo.min = result[3];
|
||||||
|
// The result[4] is PRL version.
|
||||||
|
|
||||||
|
ICCUtilsHelper.handleICCInfoChange();
|
||||||
|
};
|
||||||
RIL[REQUEST_CDMA_WRITE_SMS_TO_RUIM] = null;
|
RIL[REQUEST_CDMA_WRITE_SMS_TO_RUIM] = null;
|
||||||
RIL[REQUEST_CDMA_DELETE_SMS_ON_RUIM] = null;
|
RIL[REQUEST_CDMA_DELETE_SMS_ON_RUIM] = null;
|
||||||
RIL[REQUEST_DEVICE_IDENTITY] = function REQUEST_DEVICE_IDENTITY(length, options) {
|
RIL[REQUEST_DEVICE_IDENTITY] = function REQUEST_DEVICE_IDENTITY(length, options) {
|
||||||
@ -10985,11 +11100,11 @@ let ICCRecordHelper = {
|
|||||||
function callback(options) {
|
function callback(options) {
|
||||||
let contact = GsmPDUHelper.readAlphaIdDiallingNumber(options.recordSize);
|
let contact = GsmPDUHelper.readAlphaIdDiallingNumber(options.recordSize);
|
||||||
if (!contact ||
|
if (!contact ||
|
||||||
(RIL.iccInfo.mbdn !== undefined &&
|
(RIL.iccInfoPrivate.mbdn !== undefined &&
|
||||||
RIL.iccInfo.mbdn === contact.number)) {
|
RIL.iccInfoPrivate.mbdn === contact.number)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RIL.iccInfo.mbdn = contact.number;
|
RIL.iccInfoPrivate.mbdn = contact.number;
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
debug("MBDN, alphaId="+contact.alphaId+" number="+contact.number);
|
debug("MBDN, alphaId="+contact.alphaId+" number="+contact.number);
|
||||||
}
|
}
|
||||||
@ -12668,6 +12783,7 @@ let RuimRecordHelper = {
|
|||||||
RIL.getIMSI();
|
RIL.getIMSI();
|
||||||
this.readCST();
|
this.readCST();
|
||||||
this.readCDMAHome();
|
this.readCDMAHome();
|
||||||
|
RIL.getCdmaSubscription();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12792,9 +12908,6 @@ let RuimRecordHelper = {
|
|||||||
// Initialize buffers. This is a separate function so that unit tests can
|
// Initialize buffers. This is a separate function so that unit tests can
|
||||||
// re-initialize the buffers at will.
|
// re-initialize the buffers at will.
|
||||||
Buf.init();
|
Buf.init();
|
||||||
Buf.setOutputStream(function (parcel) {
|
|
||||||
postRILMessage(CLIENT_ID, parcel);
|
|
||||||
});
|
|
||||||
|
|
||||||
function onRILMessage(data) {
|
function onRILMessage(data) {
|
||||||
Buf.processIncoming(data);
|
Buf.processIncoming(data);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -479,11 +479,13 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mConnection->CleanUp();
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DBusThread* mConnection;
|
nsRefPtr<DBusThread> mConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
static StaticRefPtr<DBusThread> gDBusThread;
|
static StaticRefPtr<DBusThread> gDBusThread;
|
||||||
@ -532,30 +534,23 @@ StopDBus()
|
|||||||
MOZ_ASSERT(!NS_IsMainThread());
|
MOZ_ASSERT(!NS_IsMainThread());
|
||||||
NS_ENSURE_TRUE(gDBusServiceThread, true);
|
NS_ENSURE_TRUE(gDBusServiceThread, true);
|
||||||
|
|
||||||
if (gDBusThread) {
|
nsRefPtr<DBusThread> dbusThread(gDBusThread);
|
||||||
|
gDBusThread = nullptr;
|
||||||
|
|
||||||
|
if (dbusThread) {
|
||||||
static const char data = DBUS_EVENT_LOOP_EXIT;
|
static const char data = DBUS_EVENT_LOOP_EXIT;
|
||||||
ssize_t wret = TEMP_FAILURE_RETRY(write(gDBusThread->mControlFdW.get(),
|
ssize_t wret = TEMP_FAILURE_RETRY(write(dbusThread->mControlFdW.get(),
|
||||||
&data, sizeof(data)));
|
&data, sizeof(data)));
|
||||||
NS_ENSURE_TRUE(wret == 1, false);
|
NS_ENSURE_TRUE(wret == 1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
nsRefPtr<nsIThread> dbusServiceThread(gDBusServiceThread);
|
||||||
LOG("DBus Thread Joining\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (NS_FAILED(gDBusServiceThread->Shutdown())) {
|
|
||||||
NS_WARNING("DBus thread shutdown failed!");
|
|
||||||
}
|
|
||||||
gDBusServiceThread = nullptr;
|
gDBusServiceThread = nullptr;
|
||||||
|
|
||||||
#ifdef DEBUG
|
nsRefPtr<nsIRunnable> runnable =
|
||||||
LOG("DBus Thread Joined\n");
|
NS_NewRunnableMethod(dbusServiceThread, &nsIThread::Shutdown);
|
||||||
#endif
|
nsresult rv = NS_DispatchToMainThread(runnable);
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
if (gDBusThread) {
|
|
||||||
gDBusThread->CleanUp();
|
|
||||||
gDBusThread = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -563,13 +558,16 @@ StopDBus()
|
|||||||
nsresult
|
nsresult
|
||||||
DispatchToDBusThread(nsIRunnable* event)
|
DispatchToDBusThread(nsIRunnable* event)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(gDBusServiceThread);
|
nsRefPtr<nsIThread> dbusServiceThread(gDBusServiceThread);
|
||||||
MOZ_ASSERT(gDBusThread);
|
nsRefPtr<DBusThread> dbusThread(gDBusThread);
|
||||||
|
|
||||||
nsresult rv = gDBusServiceThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
NS_ENSURE_TRUE(dbusServiceThread.get() && dbusThread.get(),
|
||||||
|
NS_ERROR_NOT_INITIALIZED);
|
||||||
|
|
||||||
|
nsresult rv = dbusServiceThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
gDBusThread->WakeUp();
|
dbusThread->WakeUp();
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,7 @@ FENNEC_JAVA_FILES = \
|
|||||||
gfx/PointUtils.java \
|
gfx/PointUtils.java \
|
||||||
gfx/ProgressiveUpdateData.java \
|
gfx/ProgressiveUpdateData.java \
|
||||||
gfx/RectUtils.java \
|
gfx/RectUtils.java \
|
||||||
|
gfx/RenderTask.java \
|
||||||
gfx/ScrollbarLayer.java \
|
gfx/ScrollbarLayer.java \
|
||||||
gfx/SimpleScaleGestureDetector.java \
|
gfx/SimpleScaleGestureDetector.java \
|
||||||
gfx/SingleTileLayer.java \
|
gfx/SingleTileLayer.java \
|
||||||
|
@ -86,22 +86,22 @@ abstract class Axis {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final float MS_PER_FRAME = 1000.0f / 60.0f;
|
static final float MS_PER_FRAME = 1000.0f / 60.0f;
|
||||||
|
static final long NS_PER_FRAME = Math.round(1000000000f / 60f);
|
||||||
private static final float FRAMERATE_MULTIPLIER = (1000f/60f) / MS_PER_FRAME;
|
private static final float FRAMERATE_MULTIPLIER = (1000f/60f) / MS_PER_FRAME;
|
||||||
private static final int FLING_VELOCITY_POINTS = 8;
|
private static final int FLING_VELOCITY_POINTS = 8;
|
||||||
|
|
||||||
// The values we use for friction are based on a 16.6ms frame, adjust them to MS_PER_FRAME:
|
// The values we use for friction are based on a 16.6ms frame, adjust them to currentNsPerFrame:
|
||||||
// FRICTION^1 = FRICTION_ADJUSTED^(16/MS_PER_FRAME)
|
static float getFrameAdjustedFriction(float baseFriction, long currentNsPerFrame) {
|
||||||
// FRICTION_ADJUSTED = e ^ ((ln(FRICTION))/FRAMERATE_MULTIPLIER)
|
float framerateMultiplier = currentNsPerFrame / NS_PER_FRAME;
|
||||||
static float getFrameAdjustedFriction(float baseFriction) {
|
return (float)Math.pow(Math.E, (Math.log(baseFriction) / framerateMultiplier));
|
||||||
return (float)Math.pow(Math.E, (Math.log(baseFriction) / FRAMERATE_MULTIPLIER));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setPrefs(Map<String, Integer> prefs) {
|
static void setPrefs(Map<String, Integer> prefs) {
|
||||||
FRICTION_SLOW = getFrameAdjustedFriction(getFloatPref(prefs, PREF_SCROLLING_FRICTION_SLOW, 850));
|
FRICTION_SLOW = getFloatPref(prefs, PREF_SCROLLING_FRICTION_SLOW, 850);
|
||||||
FRICTION_FAST = getFrameAdjustedFriction(getFloatPref(prefs, PREF_SCROLLING_FRICTION_FAST, 970));
|
FRICTION_FAST = getFloatPref(prefs, PREF_SCROLLING_FRICTION_FAST, 970);
|
||||||
VELOCITY_THRESHOLD = 10 / FRAMERATE_MULTIPLIER;
|
VELOCITY_THRESHOLD = 10 / FRAMERATE_MULTIPLIER;
|
||||||
MAX_EVENT_ACCELERATION = getFloatPref(prefs, PREF_SCROLLING_MAX_EVENT_ACCELERATION, GeckoAppShell.getDpi() > 300 ? 100 : 40);
|
MAX_EVENT_ACCELERATION = getFloatPref(prefs, PREF_SCROLLING_MAX_EVENT_ACCELERATION, GeckoAppShell.getDpi() > 300 ? 100 : 40);
|
||||||
OVERSCROLL_DECEL_RATE = getFrameAdjustedFriction(getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_DECEL_RATE, 40));
|
OVERSCROLL_DECEL_RATE = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_DECEL_RATE, 40);
|
||||||
SNAP_LIMIT = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT, 300);
|
SNAP_LIMIT = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT, 300);
|
||||||
MIN_SCROLLABLE_DISTANCE = getFloatPref(prefs, PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE, 500);
|
MIN_SCROLLABLE_DISTANCE = getFloatPref(prefs, PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE, 500);
|
||||||
Log.i(LOGTAG, "Prefs: " + FRICTION_SLOW + "," + FRICTION_FAST + "," + VELOCITY_THRESHOLD + ","
|
Log.i(LOGTAG, "Prefs: " + FRICTION_SLOW + "," + FRICTION_FAST + "," + VELOCITY_THRESHOLD + ","
|
||||||
@ -314,7 +314,7 @@ abstract class Axis {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Advances a fling animation by one step. */
|
/* Advances a fling animation by one step. */
|
||||||
boolean advanceFling() {
|
boolean advanceFling(long realNsPerFrame) {
|
||||||
if (mFlingState != FlingStates.FLINGING) {
|
if (mFlingState != FlingStates.FLINGING) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -337,18 +337,20 @@ abstract class Axis {
|
|||||||
if (mDisableSnap || FloatUtils.fuzzyEquals(excess, 0.0f) || decreasingOverscroll) {
|
if (mDisableSnap || FloatUtils.fuzzyEquals(excess, 0.0f) || decreasingOverscroll) {
|
||||||
// If we aren't overscrolled, just apply friction.
|
// If we aren't overscrolled, just apply friction.
|
||||||
if (Math.abs(mVelocity) >= VELOCITY_THRESHOLD) {
|
if (Math.abs(mVelocity) >= VELOCITY_THRESHOLD) {
|
||||||
mVelocity *= FRICTION_FAST;
|
mVelocity *= getFrameAdjustedFriction(FRICTION_FAST, realNsPerFrame);
|
||||||
} else {
|
} else {
|
||||||
float t = mVelocity / VELOCITY_THRESHOLD;
|
float t = mVelocity / VELOCITY_THRESHOLD;
|
||||||
mVelocity *= FloatUtils.interpolate(FRICTION_SLOW, FRICTION_FAST, t);
|
mVelocity *= FloatUtils.interpolate(getFrameAdjustedFriction(FRICTION_SLOW, realNsPerFrame),
|
||||||
|
getFrameAdjustedFriction(FRICTION_FAST, realNsPerFrame), t);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, decrease the velocity linearly.
|
// Otherwise, decrease the velocity linearly.
|
||||||
float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT);
|
float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT);
|
||||||
|
float overscrollDecelRate = getFrameAdjustedFriction(OVERSCROLL_DECEL_RATE, realNsPerFrame);
|
||||||
if (overscroll == Overscroll.MINUS) {
|
if (overscroll == Overscroll.MINUS) {
|
||||||
mVelocity = Math.min((mVelocity + OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
|
mVelocity = Math.min((mVelocity + overscrollDecelRate) * elasticity, 0.0f);
|
||||||
} else { // must be Overscroll.PLUS
|
} else { // must be Overscroll.PLUS
|
||||||
mVelocity = Math.max((mVelocity - OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
|
mVelocity = Math.max((mVelocity - overscrollDecelRate) * elasticity, 0.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +85,7 @@ public class GLController {
|
|||||||
|
|
||||||
synchronized void surfaceDestroyed() {
|
synchronized void surfaceDestroyed() {
|
||||||
ThreadUtils.assertOnUiThread();
|
ThreadUtils.assertOnUiThread();
|
||||||
|
Log.w(LOGTAG, "GLController::surfaceDestroyed() with mCompositorCreated=" + mCompositorCreated);
|
||||||
|
|
||||||
mSurfaceValid = false;
|
mSurfaceValid = false;
|
||||||
mEGLSurface = null;
|
mEGLSurface = null;
|
||||||
@ -100,10 +101,12 @@ public class GLController {
|
|||||||
if (mCompositorCreated) {
|
if (mCompositorCreated) {
|
||||||
GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createCompositorPauseEvent());
|
GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createCompositorPauseEvent());
|
||||||
}
|
}
|
||||||
|
Log.w(LOGTAG, "done GLController::surfaceDestroyed()");
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void surfaceChanged(int newWidth, int newHeight) {
|
synchronized void surfaceChanged(int newWidth, int newHeight) {
|
||||||
ThreadUtils.assertOnUiThread();
|
ThreadUtils.assertOnUiThread();
|
||||||
|
Log.w(LOGTAG, "GLController::surfaceChanged(" + newWidth + ", " + newHeight + ") with mSurfaceValid=" + mSurfaceValid);
|
||||||
|
|
||||||
mWidth = newWidth;
|
mWidth = newWidth;
|
||||||
mHeight = newHeight;
|
mHeight = newHeight;
|
||||||
@ -113,6 +116,7 @@ public class GLController {
|
|||||||
// paused (e.g. during an orientation change), to make the compositor
|
// paused (e.g. during an orientation change), to make the compositor
|
||||||
// aware of the changed surface.
|
// aware of the changed surface.
|
||||||
resumeCompositor(mWidth, mHeight);
|
resumeCompositor(mWidth, mHeight);
|
||||||
|
Log.w(LOGTAG, "done GLController::surfaceChanged with compositor resume");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mSurfaceValid = true;
|
mSurfaceValid = true;
|
||||||
@ -130,6 +134,7 @@ public class GLController {
|
|||||||
mView.post(new Runnable() {
|
mView.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
Log.w(LOGTAG, "GLController::surfaceChanged, creating compositor; mCompositorCreated=" + mCompositorCreated + ", mSurfaceValid=" + mSurfaceValid);
|
||||||
// If we haven't yet created the compositor, and the GfxInfoThread
|
// If we haven't yet created the compositor, and the GfxInfoThread
|
||||||
// isn't done it's data gathering activities, then postpone creating
|
// isn't done it's data gathering activities, then postpone creating
|
||||||
// the compositor a little bit more. Don't block though, since this is
|
// the compositor a little bit more. Don't block though, since this is
|
||||||
@ -171,12 +176,14 @@ public class GLController {
|
|||||||
|
|
||||||
void createCompositor() {
|
void createCompositor() {
|
||||||
ThreadUtils.assertOnUiThread();
|
ThreadUtils.assertOnUiThread();
|
||||||
|
Log.w(LOGTAG, "GLController::createCompositor with mCompositorCreated=" + mCompositorCreated);
|
||||||
|
|
||||||
if (mCompositorCreated) {
|
if (mCompositorCreated) {
|
||||||
// If the compositor has already been created, just resume it instead. We don't need
|
// If the compositor has already been created, just resume it instead. We don't need
|
||||||
// to block here because if the surface is destroyed before the compositor grabs it,
|
// to block here because if the surface is destroyed before the compositor grabs it,
|
||||||
// we can handle that gracefully (i.e. the compositor will remain paused).
|
// we can handle that gracefully (i.e. the compositor will remain paused).
|
||||||
resumeCompositor(mWidth, mHeight);
|
resumeCompositor(mWidth, mHeight);
|
||||||
|
Log.w(LOGTAG, "done GLController::createCompositor with compositor resume");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,9 +194,11 @@ public class GLController {
|
|||||||
if (mEGLSurface != null && GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
|
if (mEGLSurface != null && GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
|
||||||
GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createCompositorCreateEvent(mWidth, mHeight));
|
GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createCompositorCreateEvent(mWidth, mHeight));
|
||||||
}
|
}
|
||||||
|
Log.w(LOGTAG, "done GLController::createCompositor");
|
||||||
}
|
}
|
||||||
|
|
||||||
void compositorCreated() {
|
void compositorCreated() {
|
||||||
|
Log.w(LOGTAG, "GLController::compositorCreated");
|
||||||
// This is invoked on the compositor thread, while the java UI thread
|
// This is invoked on the compositor thread, while the java UI thread
|
||||||
// is blocked on the gecko sync event in createCompositor() above
|
// is blocked on the gecko sync event in createCompositor() above
|
||||||
mCompositorCreated = true;
|
mCompositorCreated = true;
|
||||||
@ -263,6 +272,7 @@ public class GLController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void resumeCompositor(int width, int height) {
|
void resumeCompositor(int width, int height) {
|
||||||
|
Log.w(LOGTAG, "GLController::resumeCompositor(" + width + ", " + height + ") and mCompositorCreated=" + mCompositorCreated);
|
||||||
// Asking Gecko to resume the compositor takes too long (see
|
// Asking Gecko to resume the compositor takes too long (see
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=735230#c23), so we
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=735230#c23), so we
|
||||||
// resume the compositor directly. We still need to inform Gecko about
|
// resume the compositor directly. We still need to inform Gecko about
|
||||||
@ -273,6 +283,7 @@ public class GLController {
|
|||||||
GeckoAppShell.scheduleResumeComposition(width, height);
|
GeckoAppShell.scheduleResumeComposition(width, height);
|
||||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createCompositorResumeEvent());
|
GeckoAppShell.sendEventToGecko(GeckoEvent.createCompositorResumeEvent());
|
||||||
}
|
}
|
||||||
|
Log.w(LOGTAG, "done GLController::resumeCompositor");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GLControllerException extends RuntimeException {
|
public static class GLControllerException extends RuntimeException {
|
||||||
|
@ -919,6 +919,19 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
|
|||||||
return mView.post(action);
|
return mView.post(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Implementation of PanZoomTarget */
|
||||||
|
@Override
|
||||||
|
public void postRenderTask(RenderTask task) {
|
||||||
|
mView.postRenderTask(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implementation of PanZoomTarget */
|
||||||
|
@Override
|
||||||
|
public void removeRenderTask(RenderTask task) {
|
||||||
|
mView.removeRenderTask(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Implementation of PanZoomTarget */
|
/** Implementation of PanZoomTarget */
|
||||||
@Override
|
@Override
|
||||||
public boolean postDelayed(Runnable action, long delayMillis) {
|
public boolean postDelayed(Runnable action, long delayMillis) {
|
||||||
|
@ -30,9 +30,6 @@ import android.view.KeyEvent;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handles the kinetic scrolling and zooming physics for a layer controller.
|
* Handles the kinetic scrolling and zooming physics for a layer controller.
|
||||||
*
|
*
|
||||||
@ -77,8 +74,8 @@ class JavaPanZoomController
|
|||||||
// The maximum zoom factor adjustment per frame of the AUTONAV animation
|
// The maximum zoom factor adjustment per frame of the AUTONAV animation
|
||||||
private static final float MAX_ZOOM_DELTA = 0.125f;
|
private static final float MAX_ZOOM_DELTA = 0.125f;
|
||||||
|
|
||||||
// Length of the bounce animation in ms
|
// The duration of the bounce animation in ns
|
||||||
private static final int BOUNCE_ANIMATION_DURATION = 250;
|
private static final int BOUNCE_ANIMATION_DURATION = 250000000;
|
||||||
|
|
||||||
private enum PanZoomState {
|
private enum PanZoomState {
|
||||||
NOTHING, /* no touch-start events received */
|
NOTHING, /* no touch-start events received */
|
||||||
@ -115,10 +112,8 @@ class JavaPanZoomController
|
|||||||
private final TouchEventHandler mTouchEventHandler;
|
private final TouchEventHandler mTouchEventHandler;
|
||||||
private final EventDispatcher mEventDispatcher;
|
private final EventDispatcher mEventDispatcher;
|
||||||
|
|
||||||
/* The timer that handles flings or bounces. */
|
/* The task that handles flings, autonav or bounces. */
|
||||||
private Timer mAnimationTimer;
|
private PanZoomRenderTask mAnimationRenderTask;
|
||||||
/* The runnable being scheduled by the animation timer. */
|
|
||||||
private AnimationRunnable mAnimationRunnable;
|
|
||||||
/* The zoom focus at the first zoom event (in page coordinates). */
|
/* The zoom focus at the first zoom event (in page coordinates). */
|
||||||
private PointF mLastZoomFocus;
|
private PointF mLastZoomFocus;
|
||||||
/* The time the last motion event took place. */
|
/* The time the last motion event took place. */
|
||||||
@ -419,7 +414,7 @@ class JavaPanZoomController
|
|||||||
private boolean handleTouchStart(MotionEvent event) {
|
private boolean handleTouchStart(MotionEvent event) {
|
||||||
// user is taking control of movement, so stop
|
// user is taking control of movement, so stop
|
||||||
// any auto-movement we have going
|
// any auto-movement we have going
|
||||||
stopAnimationTimer();
|
stopAnimationTask();
|
||||||
|
|
||||||
switch (mState) {
|
switch (mState) {
|
||||||
case ANIMATED_ZOOM:
|
case ANIMATED_ZOOM:
|
||||||
@ -606,7 +601,7 @@ class JavaPanZoomController
|
|||||||
|
|
||||||
if (mState == PanZoomState.NOTHING) {
|
if (mState == PanZoomState.NOTHING) {
|
||||||
setState(PanZoomState.AUTONAV);
|
setState(PanZoomState.AUTONAV);
|
||||||
startAnimationTimer(new AutonavRunnable());
|
startAnimationRenderTask(new AutonavRenderTask());
|
||||||
}
|
}
|
||||||
if (mState == PanZoomState.AUTONAV) {
|
if (mState == PanZoomState.AUTONAV) {
|
||||||
mX.setAutoscrollVelocity(velocityX);
|
mX.setAutoscrollVelocity(velocityX);
|
||||||
@ -732,18 +727,18 @@ class JavaPanZoomController
|
|||||||
private void fling() {
|
private void fling() {
|
||||||
updatePosition();
|
updatePosition();
|
||||||
|
|
||||||
stopAnimationTimer();
|
stopAnimationTask();
|
||||||
|
|
||||||
boolean stopped = stopped();
|
boolean stopped = stopped();
|
||||||
mX.startFling(stopped);
|
mX.startFling(stopped);
|
||||||
mY.startFling(stopped);
|
mY.startFling(stopped);
|
||||||
|
|
||||||
startAnimationTimer(new FlingRunnable());
|
startAnimationRenderTask(new FlingRenderTask());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Performs a bounce-back animation to the given viewport metrics. */
|
/* Performs a bounce-back animation to the given viewport metrics. */
|
||||||
private void bounce(ImmutableViewportMetrics metrics, PanZoomState state) {
|
private void bounce(ImmutableViewportMetrics metrics, PanZoomState state) {
|
||||||
stopAnimationTimer();
|
stopAnimationTask();
|
||||||
|
|
||||||
ImmutableViewportMetrics bounceStartMetrics = getMetrics();
|
ImmutableViewportMetrics bounceStartMetrics = getMetrics();
|
||||||
if (bounceStartMetrics.fuzzyEquals(metrics)) {
|
if (bounceStartMetrics.fuzzyEquals(metrics)) {
|
||||||
@ -758,7 +753,7 @@ class JavaPanZoomController
|
|||||||
// setAnimationTarget to set the new final display port and not have it get
|
// setAnimationTarget to set the new final display port and not have it get
|
||||||
// clobbered by display ports from intermediate animation frames.
|
// clobbered by display ports from intermediate animation frames.
|
||||||
mTarget.setAnimationTarget(metrics);
|
mTarget.setAnimationTarget(metrics);
|
||||||
startAnimationTimer(new BounceRunnable(bounceStartMetrics, metrics));
|
startAnimationRenderTask(new BounceRenderTask(bounceStartMetrics, metrics));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Performs a bounce-back animation to the nearest valid viewport metrics. */
|
/* Performs a bounce-back animation to the nearest valid viewport metrics. */
|
||||||
@ -767,29 +762,22 @@ class JavaPanZoomController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Starts the fling or bounce animation. */
|
/* Starts the fling or bounce animation. */
|
||||||
private void startAnimationTimer(final AnimationRunnable runnable) {
|
private void startAnimationRenderTask(final PanZoomRenderTask task) {
|
||||||
if (mAnimationTimer != null) {
|
if (mAnimationRenderTask != null) {
|
||||||
Log.e(LOGTAG, "Attempted to start a new timer without canceling the old one!");
|
Log.e(LOGTAG, "Attempted to start a new task without canceling the old one!");
|
||||||
stopAnimationTimer();
|
stopAnimationTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimationTimer = new Timer("Animation Timer");
|
mAnimationRenderTask = task;
|
||||||
mAnimationRunnable = runnable;
|
mTarget.postRenderTask(mAnimationRenderTask);
|
||||||
mAnimationTimer.scheduleAtFixedRate(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() { mTarget.post(runnable); }
|
|
||||||
}, 0, (int)Axis.MS_PER_FRAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stops the fling or bounce animation. */
|
/* Stops the fling or bounce animation. */
|
||||||
private void stopAnimationTimer() {
|
private void stopAnimationTask() {
|
||||||
if (mAnimationTimer != null) {
|
if (mAnimationRenderTask != null) {
|
||||||
mAnimationTimer.cancel();
|
mAnimationRenderTask.terminate();
|
||||||
mAnimationTimer = null;
|
mTarget.removeRenderTask(mAnimationRenderTask);
|
||||||
}
|
mAnimationRenderTask = null;
|
||||||
if (mAnimationRunnable != null) {
|
|
||||||
mAnimationRunnable.terminate();
|
|
||||||
mAnimationRunnable = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -830,33 +818,64 @@ class JavaPanZoomController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract class AnimationRunnable implements Runnable {
|
/**
|
||||||
private boolean mAnimationTerminated;
|
* This class is an implementation of RenderTask which enforces its implementor to run in the UI thread.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private abstract class PanZoomRenderTask extends RenderTask {
|
||||||
|
|
||||||
/* This should always run on the UI thread */
|
/**
|
||||||
@Override
|
* the time when the current frame was started in ns.
|
||||||
public final void run() {
|
*/
|
||||||
/*
|
protected long mCurrentFrameStartTime;
|
||||||
* Since the animation timer queues this runnable on the UI thread, it
|
/**
|
||||||
* is possible that even when the animation timer is cancelled, there
|
* The current frame duration in ns.
|
||||||
* are multiple instances of this queued, so we need to have another
|
*/
|
||||||
* mechanism to abort. This is done by using the mAnimationTerminated flag.
|
protected long mLastFrameTimeDelta;
|
||||||
*/
|
|
||||||
if (mAnimationTerminated) {
|
private final Runnable mRunnable = new Runnable() {
|
||||||
return;
|
@Override
|
||||||
|
public final void run() {
|
||||||
|
if (mContinueAnimation) {
|
||||||
|
animateFrame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
animateFrame();
|
};
|
||||||
|
|
||||||
|
private boolean mContinueAnimation = true;
|
||||||
|
|
||||||
|
public PanZoomRenderTask() {
|
||||||
|
super(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final boolean internalRun(long timeDelta, long currentFrameStartTime) {
|
||||||
|
|
||||||
|
mCurrentFrameStartTime = currentFrameStartTime;
|
||||||
|
mLastFrameTimeDelta = timeDelta;
|
||||||
|
|
||||||
|
mTarget.post(mRunnable);
|
||||||
|
return mContinueAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method subclasses must override. This method is run on the UI thread thanks to internalRun
|
||||||
|
*/
|
||||||
protected abstract void animateFrame();
|
protected abstract void animateFrame();
|
||||||
|
|
||||||
/* This should always run on the UI thread */
|
/**
|
||||||
protected final void terminate() {
|
* Terminate the animation.
|
||||||
mAnimationTerminated = true;
|
*/
|
||||||
|
public void terminate() {
|
||||||
|
mContinueAnimation = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AutonavRunnable extends AnimationRunnable {
|
private class AutonavRenderTask extends PanZoomRenderTask {
|
||||||
|
public AutonavRenderTask() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void animateFrame() {
|
protected void animateFrame() {
|
||||||
if (mState != PanZoomState.AUTONAV) {
|
if (mState != PanZoomState.AUTONAV) {
|
||||||
@ -871,18 +890,20 @@ class JavaPanZoomController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The callback that performs the bounce animation. */
|
/* The task that performs the bounce animation. */
|
||||||
private class BounceRunnable extends AnimationRunnable {
|
private class BounceRenderTask extends PanZoomRenderTask {
|
||||||
/* The current frame of the bounce-back animation */
|
|
||||||
private int mBounceFrame;
|
|
||||||
/*
|
/*
|
||||||
* The viewport metrics that represent the start and end of the bounce-back animation,
|
* The viewport metrics that represent the start and end of the bounce-back animation,
|
||||||
* respectively.
|
* respectively.
|
||||||
*/
|
*/
|
||||||
private ImmutableViewportMetrics mBounceStartMetrics;
|
private ImmutableViewportMetrics mBounceStartMetrics;
|
||||||
private ImmutableViewportMetrics mBounceEndMetrics;
|
private ImmutableViewportMetrics mBounceEndMetrics;
|
||||||
|
// How long ago this bounce was started in ns.
|
||||||
|
private long mBounceDuration;
|
||||||
|
|
||||||
BounceRunnable(ImmutableViewportMetrics startMetrics, ImmutableViewportMetrics endMetrics) {
|
BounceRenderTask(ImmutableViewportMetrics startMetrics, ImmutableViewportMetrics endMetrics) {
|
||||||
|
super();
|
||||||
mBounceStartMetrics = startMetrics;
|
mBounceStartMetrics = startMetrics;
|
||||||
mBounceEndMetrics = endMetrics;
|
mBounceEndMetrics = endMetrics;
|
||||||
}
|
}
|
||||||
@ -900,7 +921,8 @@ class JavaPanZoomController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Perform the next frame of the bounce-back animation. */
|
/* Perform the next frame of the bounce-back animation. */
|
||||||
if (mBounceFrame < (int)(BOUNCE_ANIMATION_DURATION / Axis.MS_PER_FRAME)) {
|
mBounceDuration = mCurrentFrameStartTime - getStartTime();
|
||||||
|
if (mBounceDuration < BOUNCE_ANIMATION_DURATION) {
|
||||||
advanceBounce();
|
advanceBounce();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -914,10 +936,9 @@ class JavaPanZoomController
|
|||||||
/* Performs one frame of a bounce animation. */
|
/* Performs one frame of a bounce animation. */
|
||||||
private void advanceBounce() {
|
private void advanceBounce() {
|
||||||
synchronized (mTarget.getLock()) {
|
synchronized (mTarget.getLock()) {
|
||||||
float t = easeOut(mBounceFrame * Axis.MS_PER_FRAME / BOUNCE_ANIMATION_DURATION);
|
float t = easeOut((float)mBounceDuration / BOUNCE_ANIMATION_DURATION);
|
||||||
ImmutableViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
|
ImmutableViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
|
||||||
mTarget.setViewportMetrics(newMetrics);
|
mTarget.setViewportMetrics(newMetrics);
|
||||||
mBounceFrame++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -925,13 +946,17 @@ class JavaPanZoomController
|
|||||||
private void finishBounce() {
|
private void finishBounce() {
|
||||||
synchronized (mTarget.getLock()) {
|
synchronized (mTarget.getLock()) {
|
||||||
mTarget.setViewportMetrics(mBounceEndMetrics);
|
mTarget.setViewportMetrics(mBounceEndMetrics);
|
||||||
mBounceFrame = -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The callback that performs the fling animation.
|
// The callback that performs the fling animation.
|
||||||
private class FlingRunnable extends AnimationRunnable {
|
private class FlingRenderTask extends PanZoomRenderTask {
|
||||||
|
|
||||||
|
public FlingRenderTask() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void animateFrame() {
|
protected void animateFrame() {
|
||||||
/*
|
/*
|
||||||
@ -945,8 +970,8 @@ class JavaPanZoomController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Advance flings, if necessary. */
|
/* Advance flings, if necessary. */
|
||||||
boolean flingingX = mX.advanceFling();
|
boolean flingingX = mX.advanceFling(mLastFrameTimeDelta);
|
||||||
boolean flingingY = mY.advanceFling();
|
boolean flingingY = mY.advanceFling(mLastFrameTimeDelta);
|
||||||
|
|
||||||
boolean overscrolled = (mX.overscrolled() || mY.overscrolled());
|
boolean overscrolled = (mX.overscrolled() || mY.overscrolled());
|
||||||
|
|
||||||
@ -983,7 +1008,7 @@ class JavaPanZoomController
|
|||||||
private void finishAnimation() {
|
private void finishAnimation() {
|
||||||
checkMainThread();
|
checkMainThread();
|
||||||
|
|
||||||
stopAnimationTimer();
|
stopAnimationTask();
|
||||||
|
|
||||||
// Force a viewport synchronisation
|
// Force a viewport synchronisation
|
||||||
mTarget.forceRedraw(null);
|
mTarget.forceRedraw(null);
|
||||||
|
@ -20,13 +20,10 @@ import android.view.animation.DecelerateInterpolator;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
public class LayerMarginsAnimator implements TouchEventInterceptor {
|
public class LayerMarginsAnimator implements TouchEventInterceptor {
|
||||||
private static final String LOGTAG = "GeckoLayerMarginsAnimator";
|
private static final String LOGTAG = "GeckoLayerMarginsAnimator";
|
||||||
private static final float MS_PER_FRAME = 1000.0f / 60.0f;
|
// The duration of the animation in ns
|
||||||
private static final long MARGIN_ANIMATION_DURATION = 250;
|
private static final long MARGIN_ANIMATION_DURATION = 250000000;
|
||||||
private static final String PREF_SHOW_MARGINS_THRESHOLD = "browser.ui.show-margins-threshold";
|
private static final String PREF_SHOW_MARGINS_THRESHOLD = "browser.ui.show-margins-threshold";
|
||||||
|
|
||||||
/* This is the proportion of the viewport rect, minus maximum margins,
|
/* This is the proportion of the viewport rect, minus maximum margins,
|
||||||
@ -40,8 +37,8 @@ public class LayerMarginsAnimator implements TouchEventInterceptor {
|
|||||||
private final RectF mMaxMargins;
|
private final RectF mMaxMargins;
|
||||||
/* If this boolean is true, scroll changes will not affect margins */
|
/* If this boolean is true, scroll changes will not affect margins */
|
||||||
private boolean mMarginsPinned;
|
private boolean mMarginsPinned;
|
||||||
/* The timer that handles showing/hiding margins */
|
/* The task that handles showing/hiding margins */
|
||||||
private Timer mAnimationTimer;
|
private LayerMarginsAnimationTask mAnimationTask;
|
||||||
/* This interpolator is used for the above mentioned animation */
|
/* This interpolator is used for the above mentioned animation */
|
||||||
private final DecelerateInterpolator mInterpolator;
|
private final DecelerateInterpolator mInterpolator;
|
||||||
/* The GeckoLayerClient whose margins will be animated */
|
/* The GeckoLayerClient whose margins will be animated */
|
||||||
@ -105,9 +102,9 @@ public class LayerMarginsAnimator implements TouchEventInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void animateMargins(final float left, final float top, final float right, final float bottom, boolean immediately) {
|
private void animateMargins(final float left, final float top, final float right, final float bottom, boolean immediately) {
|
||||||
if (mAnimationTimer != null) {
|
if (mAnimationTask != null) {
|
||||||
mAnimationTimer.cancel();
|
mTarget.getView().removeRenderTask(mAnimationTask);
|
||||||
mAnimationTimer = null;
|
mAnimationTask = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (immediately) {
|
if (immediately) {
|
||||||
@ -118,47 +115,8 @@ public class LayerMarginsAnimator implements TouchEventInterceptor {
|
|||||||
|
|
||||||
ImmutableViewportMetrics metrics = mTarget.getViewportMetrics();
|
ImmutableViewportMetrics metrics = mTarget.getViewportMetrics();
|
||||||
|
|
||||||
final long startTime = SystemClock.uptimeMillis();
|
mAnimationTask = new LayerMarginsAnimationTask(false, metrics, left, top, right, bottom);
|
||||||
final float startLeft = metrics.marginLeft;
|
mTarget.getView().postRenderTask(mAnimationTask);
|
||||||
final float startTop = metrics.marginTop;
|
|
||||||
final float startRight = metrics.marginRight;
|
|
||||||
final float startBottom = metrics.marginBottom;
|
|
||||||
|
|
||||||
mAnimationTimer = new Timer("Margin Animation Timer");
|
|
||||||
mAnimationTimer.scheduleAtFixedRate(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
float progress = mInterpolator.getInterpolation(
|
|
||||||
Math.min(1.0f, (SystemClock.uptimeMillis() - startTime)
|
|
||||||
/ (float)MARGIN_ANIMATION_DURATION));
|
|
||||||
|
|
||||||
synchronized(mTarget.getLock()) {
|
|
||||||
ImmutableViewportMetrics oldMetrics = mTarget.getViewportMetrics();
|
|
||||||
ImmutableViewportMetrics newMetrics = oldMetrics.setMargins(
|
|
||||||
FloatUtils.interpolate(startLeft, left, progress),
|
|
||||||
FloatUtils.interpolate(startTop, top, progress),
|
|
||||||
FloatUtils.interpolate(startRight, right, progress),
|
|
||||||
FloatUtils.interpolate(startBottom, bottom, progress));
|
|
||||||
PointF oldOffset = oldMetrics.getMarginOffset();
|
|
||||||
PointF newOffset = newMetrics.getMarginOffset();
|
|
||||||
newMetrics =
|
|
||||||
newMetrics.offsetViewportByAndClamp(newOffset.x - oldOffset.x,
|
|
||||||
newOffset.y - oldOffset.y);
|
|
||||||
|
|
||||||
if (progress >= 1.0f) {
|
|
||||||
if (mAnimationTimer != null) {
|
|
||||||
mAnimationTimer.cancel();
|
|
||||||
mAnimationTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force a redraw and update Gecko
|
|
||||||
mTarget.forceViewportMetrics(newMetrics, true, true);
|
|
||||||
} else {
|
|
||||||
mTarget.forceViewportMetrics(newMetrics, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 0, (int)MS_PER_FRAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,9 +199,9 @@ public class LayerMarginsAnimator implements TouchEventInterceptor {
|
|||||||
// Only alter margins if the toolbar isn't pinned
|
// Only alter margins if the toolbar isn't pinned
|
||||||
if (!mMarginsPinned) {
|
if (!mMarginsPinned) {
|
||||||
// Make sure to cancel any margin animations when margin-scrolling begins
|
// Make sure to cancel any margin animations when margin-scrolling begins
|
||||||
if (mAnimationTimer != null) {
|
if (mAnimationTask != null) {
|
||||||
mAnimationTimer.cancel();
|
mTarget.getView().removeRenderTask(mAnimationTask);
|
||||||
mAnimationTimer = null;
|
mAnimationTask = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the touch travel when changing direction
|
// Reset the touch travel when changing direction
|
||||||
@ -297,4 +255,62 @@ public class LayerMarginsAnimator implements TouchEventInterceptor {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LayerMarginsAnimationTask extends RenderTask {
|
||||||
|
private float mStartLeft, mStartTop, mStartRight, mStartBottom;
|
||||||
|
private float mTop, mBottom, mLeft, mRight;
|
||||||
|
private boolean mContinueAnimation;
|
||||||
|
|
||||||
|
public LayerMarginsAnimationTask(boolean runAfter, ImmutableViewportMetrics metrics,
|
||||||
|
float left, float top, float right, float bottom) {
|
||||||
|
super(runAfter);
|
||||||
|
mContinueAnimation = true;
|
||||||
|
this.mStartLeft = metrics.marginLeft;
|
||||||
|
this.mStartTop = metrics.marginTop;
|
||||||
|
this.mStartRight = metrics.marginRight;
|
||||||
|
this.mStartBottom = metrics.marginBottom;
|
||||||
|
this.mLeft = left;
|
||||||
|
this.mRight = right;
|
||||||
|
this.mTop = top;
|
||||||
|
this.mBottom = bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean internalRun(long timeDelta, long currentFrameStartTime) {
|
||||||
|
if (!mContinueAnimation) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the progress (between 0 and 1)
|
||||||
|
float progress = mInterpolator.getInterpolation(
|
||||||
|
Math.min(1.0f, (System.nanoTime() - getStartTime())
|
||||||
|
/ (float)MARGIN_ANIMATION_DURATION));
|
||||||
|
|
||||||
|
// Calculate the new metrics accordingly
|
||||||
|
synchronized (mTarget.getLock()) {
|
||||||
|
ImmutableViewportMetrics oldMetrics = mTarget.getViewportMetrics();
|
||||||
|
ImmutableViewportMetrics newMetrics = oldMetrics.setMargins(
|
||||||
|
FloatUtils.interpolate(mStartLeft, mLeft, progress),
|
||||||
|
FloatUtils.interpolate(mStartTop, mTop, progress),
|
||||||
|
FloatUtils.interpolate(mStartRight, mRight, progress),
|
||||||
|
FloatUtils.interpolate(mStartBottom, mBottom, progress));
|
||||||
|
PointF oldOffset = oldMetrics.getMarginOffset();
|
||||||
|
PointF newOffset = newMetrics.getMarginOffset();
|
||||||
|
newMetrics =
|
||||||
|
newMetrics.offsetViewportByAndClamp(newOffset.x - oldOffset.x,
|
||||||
|
newOffset.y - oldOffset.y);
|
||||||
|
|
||||||
|
if (progress >= 1.0f) {
|
||||||
|
mContinueAnimation = false;
|
||||||
|
|
||||||
|
// Force a redraw and update Gecko
|
||||||
|
mTarget.forceViewportMetrics(newMetrics, true, true);
|
||||||
|
} else {
|
||||||
|
mTarget.forceViewportMetrics(newMetrics, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mContinueAnimation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import org.mozilla.gecko.R;
|
|||||||
import org.mozilla.gecko.Tab;
|
import org.mozilla.gecko.Tab;
|
||||||
import org.mozilla.gecko.Tabs;
|
import org.mozilla.gecko.Tabs;
|
||||||
import org.mozilla.gecko.gfx.Layer.RenderContext;
|
import org.mozilla.gecko.gfx.Layer.RenderContext;
|
||||||
|
import org.mozilla.gecko.gfx.RenderTask;
|
||||||
import org.mozilla.gecko.mozglue.DirectBufferAllocator;
|
import org.mozilla.gecko.mozglue.DirectBufferAllocator;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -50,6 +51,9 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
|
|||||||
private static final int FRAME_RATE_METER_WIDTH = 128;
|
private static final int FRAME_RATE_METER_WIDTH = 128;
|
||||||
private static final int FRAME_RATE_METER_HEIGHT = 32;
|
private static final int FRAME_RATE_METER_HEIGHT = 32;
|
||||||
|
|
||||||
|
private static final long NANOS_PER_MS = 1000000;
|
||||||
|
private static final int NANOS_PER_SECOND = 1000000000;
|
||||||
|
|
||||||
private final LayerView mView;
|
private final LayerView mView;
|
||||||
private final NinePatchTileLayer mShadowLayer;
|
private final NinePatchTileLayer mShadowLayer;
|
||||||
private TextLayer mFrameRateLayer;
|
private TextLayer mFrameRateLayer;
|
||||||
@ -63,6 +67,9 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
|
|||||||
private int mBackgroundColor;
|
private int mBackgroundColor;
|
||||||
private int mOverscrollColor;
|
private int mOverscrollColor;
|
||||||
|
|
||||||
|
private long mLastFrameTime;
|
||||||
|
private final CopyOnWriteArrayList<RenderTask> mTasks;
|
||||||
|
|
||||||
private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>();
|
private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>();
|
||||||
|
|
||||||
// Dropped frames display
|
// Dropped frames display
|
||||||
@ -138,6 +145,10 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
|
|||||||
Bitmap scrollbarImage = view.getScrollbarImage();
|
Bitmap scrollbarImage = view.getScrollbarImage();
|
||||||
IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight());
|
IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight());
|
||||||
scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size);
|
scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size);
|
||||||
|
|
||||||
|
mTasks = new CopyOnWriteArrayList<RenderTask>();
|
||||||
|
mLastFrameTime = System.nanoTime();
|
||||||
|
|
||||||
mVertScrollLayer = new ScrollbarLayer(this, scrollbarImage, size, true);
|
mVertScrollLayer = new ScrollbarLayer(this, scrollbarImage, size, true);
|
||||||
mHorizScrollLayer = new ScrollbarLayer(this, diagonalFlip(scrollbarImage), new IntSize(size.height, size.width), false);
|
mHorizScrollLayer = new ScrollbarLayer(this, diagonalFlip(scrollbarImage), new IntSize(size.height, size.width), false);
|
||||||
mFadeRunnable = new FadeRunnable();
|
mFadeRunnable = new FadeRunnable();
|
||||||
@ -241,6 +252,30 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
|
|||||||
return mMaxTextureSize;
|
return mMaxTextureSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void postRenderTask(RenderTask aTask) {
|
||||||
|
mTasks.add(aTask);
|
||||||
|
mView.requestRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeRenderTask(RenderTask aTask) {
|
||||||
|
mTasks.remove(aTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runRenderTasks(CopyOnWriteArrayList<RenderTask> tasks, boolean after, long frameStartTime) {
|
||||||
|
for (RenderTask task : tasks) {
|
||||||
|
if (task.runAfter != after) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean stillRunning = task.run(frameStartTime - mLastFrameTime, frameStartTime);
|
||||||
|
|
||||||
|
// Remove the task from the list if its finished
|
||||||
|
if (!stillRunning) {
|
||||||
|
tasks.remove(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addLayer(Layer layer) {
|
public void addLayer(Layer layer) {
|
||||||
synchronized (mExtraLayers) {
|
synchronized (mExtraLayers) {
|
||||||
if (mExtraLayers.contains(layer)) {
|
if (mExtraLayers.contains(layer)) {
|
||||||
@ -299,7 +334,7 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateDroppedFrames(long frameStartTime) {
|
private void updateDroppedFrames(long frameStartTime) {
|
||||||
int frameElapsedTime = (int)(SystemClock.uptimeMillis() - frameStartTime);
|
int frameElapsedTime = (int)((System.nanoTime() - frameStartTime) / NANOS_PER_MS);
|
||||||
|
|
||||||
/* Update the running statistics. */
|
/* Update the running statistics. */
|
||||||
mFrameTimingsSum -= mFrameTimings[mCurrentFrame];
|
mFrameTimingsSum -= mFrameTimings[mCurrentFrame];
|
||||||
@ -456,7 +491,7 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
|
|||||||
|
|
||||||
/** This function is invoked via JNI; be careful when modifying signature. */
|
/** This function is invoked via JNI; be careful when modifying signature. */
|
||||||
public void beginDrawing() {
|
public void beginDrawing() {
|
||||||
mFrameStartTime = SystemClock.uptimeMillis();
|
mFrameStartTime = System.nanoTime();
|
||||||
|
|
||||||
TextureReaper.get().reap();
|
TextureReaper.get().reap();
|
||||||
TextureGenerator.get().fill();
|
TextureGenerator.get().fill();
|
||||||
@ -465,6 +500,9 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
|
|||||||
|
|
||||||
Layer rootLayer = mView.getLayerClient().getRoot();
|
Layer rootLayer = mView.getLayerClient().getRoot();
|
||||||
|
|
||||||
|
// Run through pre-render tasks
|
||||||
|
runRenderTasks(mTasks, false, mFrameStartTime);
|
||||||
|
|
||||||
if (!mPageContext.fuzzyEquals(mLastPageContext) && !mView.isFullScreen()) {
|
if (!mPageContext.fuzzyEquals(mLastPageContext) && !mView.isFullScreen()) {
|
||||||
// The viewport or page changed, so show the scrollbars again
|
// The viewport or page changed, so show the scrollbars again
|
||||||
// as per UX decision. Don't do this if we're in full-screen mode though.
|
// as per UX decision. Don't do this if we're in full-screen mode though.
|
||||||
@ -604,12 +642,14 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
|
|||||||
mCompleteFramesRendered += 1.0f - checkerboard;
|
mCompleteFramesRendered += 1.0f - checkerboard;
|
||||||
mFramesRendered ++;
|
mFramesRendered ++;
|
||||||
|
|
||||||
if (mFrameStartTime - mProfileOutputTime > 1000) {
|
if (mFrameStartTime - mProfileOutputTime > NANOS_PER_SECOND) {
|
||||||
mProfileOutputTime = mFrameStartTime;
|
mProfileOutputTime = mFrameStartTime;
|
||||||
printCheckerboardStats();
|
printCheckerboardStats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runRenderTasks(mTasks, true, mFrameStartTime);
|
||||||
|
|
||||||
/* Draw the FPS. */
|
/* Draw the FPS. */
|
||||||
if (mFrameRateLayer != null) {
|
if (mFrameRateLayer != null) {
|
||||||
updateDroppedFrames(mFrameStartTime);
|
updateDroppedFrames(mFrameStartTime);
|
||||||
@ -652,6 +692,7 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
|
|||||||
});
|
});
|
||||||
mView.setPaintState(LayerView.PAINT_AFTER_FIRST);
|
mView.setPaintState(LayerView.PAINT_AFTER_FIRST);
|
||||||
}
|
}
|
||||||
|
mLastFrameTime = mFrameStartTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,6 +401,14 @@ public class LayerView extends FrameLayout {
|
|||||||
mRenderer.removeLayer(layer);
|
mRenderer.removeLayer(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void postRenderTask(RenderTask task) {
|
||||||
|
mRenderer.postRenderTask(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeRenderTask(RenderTask task) {
|
||||||
|
mRenderer.removeRenderTask(task);
|
||||||
|
}
|
||||||
|
|
||||||
public int getMaxTextureSize() {
|
public int getMaxTextureSize() {
|
||||||
return mRenderer.getMaxTextureSize();
|
return mRenderer.getMaxTextureSize();
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ public interface PanZoomTarget {
|
|||||||
|
|
||||||
public boolean post(Runnable action);
|
public boolean post(Runnable action);
|
||||||
public boolean postDelayed(Runnable action, long delayMillis);
|
public boolean postDelayed(Runnable action, long delayMillis);
|
||||||
|
public void postRenderTask(RenderTask task);
|
||||||
|
public void removeRenderTask(RenderTask task);
|
||||||
public Object getLock();
|
public Object getLock();
|
||||||
public PointF convertViewPointToLayerPoint(PointF viewPoint);
|
public PointF convertViewPointToLayerPoint(PointF viewPoint);
|
||||||
}
|
}
|
||||||
|
80
mobile/android/base/gfx/RenderTask.java
Normal file
80
mobile/android/base/gfx/RenderTask.java
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.gfx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class used to schedule a callback to occur when the next frame is drawn.
|
||||||
|
* Subclasses must redefine the internalRun method, not the run method.
|
||||||
|
*/
|
||||||
|
public abstract class RenderTask {
|
||||||
|
/**
|
||||||
|
* Whether to run the task after the render, or before.
|
||||||
|
*/
|
||||||
|
public final boolean runAfter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time when this task has first run, in ns. Useful for tasks which run for a specific duration.
|
||||||
|
*/
|
||||||
|
private long mStartTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we should initialise mStartTime on the next frame run.
|
||||||
|
*/
|
||||||
|
private boolean mResetStartTime = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The callback to run on each frame. timeDelta is the time elapsed since
|
||||||
|
* the last call, in nanoseconds. Returns true if it should continue
|
||||||
|
* running, or false if it should be removed from the task queue. Returning
|
||||||
|
* true implicitly schedules a redraw.
|
||||||
|
*
|
||||||
|
* This method first initializes the start time if resetStartTime has been invoked,
|
||||||
|
* then calls internalRun.
|
||||||
|
*
|
||||||
|
* Note : subclasses should override internalRun.
|
||||||
|
*
|
||||||
|
* @param timeDelta the time between the beginning of last frame and the beginning of this frame, in ns.
|
||||||
|
* @param currentFrameStartTime the startTime of the current frame, in ns.
|
||||||
|
* @return true if animation should be run at the next frame, false otherwise
|
||||||
|
* @see RenderTask#internalRun(long, long)
|
||||||
|
*/
|
||||||
|
public final boolean run(long timeDelta, long currentFrameStartTime) {
|
||||||
|
if (mResetStartTime) {
|
||||||
|
mStartTime = currentFrameStartTime;
|
||||||
|
mResetStartTime = false;
|
||||||
|
}
|
||||||
|
return internalRun(timeDelta, currentFrameStartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract method to be overridden by subclasses.
|
||||||
|
* @param timeDelta the time between the beginning of last frame and the beginning of this frame, in ns
|
||||||
|
* @param currentFrameStartTime the startTime of the current frame, in ns.
|
||||||
|
* @return true if animation should be run at the next frame, false otherwise
|
||||||
|
*/
|
||||||
|
protected abstract boolean internalRun(long timeDelta, long currentFrameStartTime);
|
||||||
|
|
||||||
|
public RenderTask(boolean aRunAfter) {
|
||||||
|
runAfter = aRunAfter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the start time of this task.
|
||||||
|
* It is the start time of the first frame this task was run on.
|
||||||
|
* @return the start time in ns
|
||||||
|
*/
|
||||||
|
public long getStartTime() {
|
||||||
|
return mStartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a reset of the recorded start time next time {@link RenderTask#run(long, long)} is run.
|
||||||
|
* @see RenderTask#getStartTime()
|
||||||
|
*/
|
||||||
|
public void resetStartTime() {
|
||||||
|
mResetStartTime = true;
|
||||||
|
}
|
||||||
|
}
|
@ -105,6 +105,10 @@ public class SearchEnginePreference extends Preference {
|
|||||||
// Create a drawable from the iconURI and assign it to this Preference for display.
|
// Create a drawable from the iconURI and assign it to this Preference for display.
|
||||||
String iconURI = geckoEngineJSON.getString("iconURI");
|
String iconURI = geckoEngineJSON.getString("iconURI");
|
||||||
Bitmap iconBitmap = BitmapUtils.getBitmapFromDataURI(iconURI);
|
Bitmap iconBitmap = BitmapUtils.getBitmapFromDataURI(iconURI);
|
||||||
|
// The favicon provided may be null or corrupt, if there was a network error or similar.
|
||||||
|
if (iconBitmap == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Bitmap scaledIconBitmap = Bitmap.createScaledBitmap(iconBitmap, sIconSize, sIconSize, false);
|
Bitmap scaledIconBitmap = Bitmap.createScaledBitmap(iconBitmap, sIconSize, sIconSize, false);
|
||||||
BitmapDrawable drawable = new BitmapDrawable(scaledIconBitmap);
|
BitmapDrawable drawable = new BitmapDrawable(scaledIconBitmap);
|
||||||
setIcon(drawable);
|
setIcon(drawable);
|
||||||
|
@ -40,6 +40,27 @@
|
|||||||
<item name="android:nextFocusUp">@+id/info</item>
|
<item name="android:nextFocusUp">@+id/info</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="Toast">
|
||||||
|
<item name="android:layout_width">300dp</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:layout_centerHorizontal">true</item>
|
||||||
|
|
||||||
|
<!-- Copied from root Toast style. Margin-left/right were removed since they
|
||||||
|
large tablets are never going to be 300dp wide. -->
|
||||||
|
<item name="android:layout_alignParentBottom">true</item>
|
||||||
|
<item name="android:layout_marginBottom">64dp</item>
|
||||||
|
<item name="android:orientation">horizontal</item>
|
||||||
|
<item name="android:background">@drawable/toast</item>
|
||||||
|
<item name="android:clickable">true</item>
|
||||||
|
<item name="android:showDividers">middle</item>
|
||||||
|
<item name="android:dividerPadding">16dp</item>
|
||||||
|
|
||||||
|
<item name="android:paddingTop">0dp</item>
|
||||||
|
<item name="android:paddingBottom">0dp</item>
|
||||||
|
<item name="android:paddingLeft">0dp</item>
|
||||||
|
<item name="android:paddingRight">0dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="Widget.BookmarksListView" parent="Widget.HomeListView">
|
<style name="Widget.BookmarksListView" parent="Widget.HomeListView">
|
||||||
<item name="android:paddingLeft">32dp</item>
|
<item name="android:paddingLeft">32dp</item>
|
||||||
<item name="android:paddingRight">32dp</item>
|
<item name="android:paddingRight">32dp</item>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[testAwesomebar]
|
[testAwesomebar]
|
||||||
# [testAwesomebarSwipes] # disabled on fig - bug 880060
|
# [testAwesomebarSwipes] # disabled on fig - bug 880060
|
||||||
# [testBookmark] # disabled on fig - bug 880060
|
# [testBookmark] # disabled on fig - bug 880060
|
||||||
# [testBookmarklets] # disabled on fig - bug 880060
|
[testBookmarklets]
|
||||||
[testBookmarkKeyword]
|
[testBookmarkKeyword]
|
||||||
[testBrowserSearchVisibility]
|
[testBrowserSearchVisibility]
|
||||||
[testJNI]
|
[testJNI]
|
||||||
|
@ -23,6 +23,7 @@ public class testBookmarklets extends AboutHomeTest {
|
|||||||
|
|
||||||
// load a standard page so bookmarklets work
|
// load a standard page so bookmarklets work
|
||||||
inputAndLoadUrl(url);
|
inputAndLoadUrl(url);
|
||||||
|
verifyPageTitle("Browser Blank Page 01"); // Waiting for page title to ensure the page is loaded
|
||||||
|
|
||||||
// verify that user-entered bookmarklets do *not* work
|
// verify that user-entered bookmarklets do *not* work
|
||||||
enterUrl(js);
|
enterUrl(js);
|
||||||
@ -39,28 +40,33 @@ public class testBookmarklets extends AboutHomeTest {
|
|||||||
// add this using the UI, so we go through the content provider.
|
// add this using the UI, so we go through the content provider.
|
||||||
addOrUpdateMobileBookmark(title, js);
|
addOrUpdateMobileBookmark(title, js);
|
||||||
|
|
||||||
// verify that bookmarklets clicked in awesomescreen work
|
// Open about:home in the Bookmarks page
|
||||||
/* Removed by Bug 896576 - [fig] Remove [getBookmarksList] from BaseTest
|
openAboutHomeTab(AboutHomeTabs.BOOKMARKS);
|
||||||
ListView bookmarks = getBookmarksList(title);
|
|
||||||
|
|
||||||
Boolean found = false;
|
ListView bookmarks = findListViewWithTag("bookmarks");
|
||||||
if (bookmarks == null) {
|
mAsserter.is(waitForListToLoad(bookmarks), true, "list is properly loaded");
|
||||||
mAsserter.is(true, true, "Did not find the bookmarks section in the awesomebar");
|
|
||||||
} else {
|
int width = mDriver.getGeckoWidth();
|
||||||
for (int i = 0; i < bookmarks.getAdapter().getCount(); i++) {
|
int height = mDriver.getGeckoHeight();
|
||||||
Cursor c = (Cursor)bookmarks.getItemAtPosition(i);
|
|
||||||
String turl = c.getString(c.getColumnIndexOrThrow("url"));
|
// Scroll down so that the bookmarks list has more items on screen.
|
||||||
if (turl.equals(js)) {
|
mActions.drag(width / 2, width / 2, height - 10, height / 2);
|
||||||
found = true;
|
|
||||||
mAsserter.is(1, 1, "Found bookmarklet added to bookmarks: " + js);
|
// Verify that bookmarklets clicked in awesomescreen work
|
||||||
mSolo.clickOnView(bookmarks.getChildAt(i));
|
boolean found = false;
|
||||||
}
|
for (int i = bookmarks.getHeaderViewsCount(); i < bookmarks.getAdapter().getCount(); i++) {
|
||||||
|
Cursor c = (Cursor)bookmarks.getItemAtPosition(i);
|
||||||
|
String aUrl = c.getString(c.getColumnIndexOrThrow("url"));
|
||||||
|
if (aUrl.equals(js)) {
|
||||||
|
found = true;
|
||||||
|
mAsserter.is(1, 1, "Found bookmarklet added to bookmarks: " + js);
|
||||||
|
mSolo.clickOnView(bookmarks.getChildAt(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
mAsserter.is(found, true, "Found the bookmark: " + js + " and clicked on it");
|
mAsserter.is(found, true, "Found the bookmark: " + js + " and clicked on it");
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
alerted = waitForTest(new BooleanTest() {
|
alerted = waitForTest(new BooleanTest() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
locale/@AB_CD@/browser/feedback.dtd (%chrome/feedback.dtd)
|
locale/@AB_CD@/browser/feedback.dtd (%chrome/feedback.dtd)
|
||||||
locale/@AB_CD@/browser/phishing.dtd (%chrome/phishing.dtd)
|
locale/@AB_CD@/browser/phishing.dtd (%chrome/phishing.dtd)
|
||||||
locale/@AB_CD@/browser/payments.properties (%chrome/payments.properties)
|
locale/@AB_CD@/browser/payments.properties (%chrome/payments.properties)
|
||||||
|
locale/@AB_CD@/browser/handling.properties (%chrome/handling.properties)
|
||||||
|
|
||||||
# overrides for toolkit l10n, also for en-US
|
# overrides for toolkit l10n, also for en-US
|
||||||
relativesrcdir toolkit/locales:
|
relativesrcdir toolkit/locales:
|
||||||
@ -39,7 +40,6 @@ relativesrcdir toolkit/locales:
|
|||||||
locale/@AB_CD@/browser/overrides/aboutAbout.dtd (%chrome/global/aboutAbout.dtd)
|
locale/@AB_CD@/browser/overrides/aboutAbout.dtd (%chrome/global/aboutAbout.dtd)
|
||||||
locale/@AB_CD@/browser/overrides/aboutRights.dtd (%chrome/global/aboutRights.dtd)
|
locale/@AB_CD@/browser/overrides/aboutRights.dtd (%chrome/global/aboutRights.dtd)
|
||||||
locale/@AB_CD@/browser/overrides/commonDialogs.properties (%chrome/global/commonDialogs.properties)
|
locale/@AB_CD@/browser/overrides/commonDialogs.properties (%chrome/global/commonDialogs.properties)
|
||||||
locale/@AB_CD@/browser/overrides/handling/handling.properties (%chrome/mozapps/handling/handling.properties)
|
|
||||||
locale/@AB_CD@/browser/overrides/intl.properties (%chrome/global/intl.properties)
|
locale/@AB_CD@/browser/overrides/intl.properties (%chrome/global/intl.properties)
|
||||||
locale/@AB_CD@/browser/overrides/intl.css (%chrome/global/intl.css)
|
locale/@AB_CD@/browser/overrides/intl.css (%chrome/global/intl.css)
|
||||||
locale/@AB_CD@/browser/overrides/passwordmgr.properties (%chrome/passwordmgr/passwordmgr.properties)
|
locale/@AB_CD@/browser/overrides/passwordmgr.properties (%chrome/passwordmgr/passwordmgr.properties)
|
||||||
@ -61,7 +61,7 @@ relativesrcdir toolkit/locales:
|
|||||||
% override chrome://global/locale/aboutAbout.dtd chrome://browser/locale/overrides/aboutAbout.dtd
|
% override chrome://global/locale/aboutAbout.dtd chrome://browser/locale/overrides/aboutAbout.dtd
|
||||||
% override chrome://global/locale/aboutRights.dtd chrome://browser/locale/overrides/aboutRights.dtd
|
% override chrome://global/locale/aboutRights.dtd chrome://browser/locale/overrides/aboutRights.dtd
|
||||||
% override chrome://global/locale/commonDialogs.properties chrome://browser/locale/overrides/commonDialogs.properties
|
% override chrome://global/locale/commonDialogs.properties chrome://browser/locale/overrides/commonDialogs.properties
|
||||||
% override chrome://mozapps/locale/handling/handling.properties chrome://browser/locale/overrides/handling/handling.properties
|
% override chrome://mozapps/locale/handling/handling.properties chrome://browser/locale/handling.properties
|
||||||
% override chrome://global/locale/intl.properties chrome://browser/locale/overrides/intl.properties
|
% override chrome://global/locale/intl.properties chrome://browser/locale/overrides/intl.properties
|
||||||
% override chrome://global/locale/intl.css chrome://browser/locale/overrides/intl.css
|
% override chrome://global/locale/intl.css chrome://browser/locale/overrides/intl.css
|
||||||
% override chrome://passwordmgr/locale/passwordmgr.properties chrome://browser/locale/overrides/passwordmgr/passwordmgr.properties
|
% override chrome://passwordmgr/locale/passwordmgr.properties chrome://browser/locale/overrides/passwordmgr/passwordmgr.properties
|
||||||
|
@ -25,6 +25,8 @@ Dashboard::Dashboard()
|
|||||||
|
|
||||||
Dashboard::~Dashboard()
|
Dashboard::~Dashboard()
|
||||||
{
|
{
|
||||||
|
if (mDnsup.cancel)
|
||||||
|
mDnsup.cancel->Cancel(NS_ERROR_ABORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
@ -403,66 +405,6 @@ Dashboard::RequestDNSInfo(NetDashboardCallback* cb)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
Dashboard::RequestDNSLookup(const nsACString &aHost, NetDashboardCallback* cb)
|
|
||||||
{
|
|
||||||
if (mDnsup.cb)
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
mDnsup.cb = cb;
|
|
||||||
nsresult rv;
|
|
||||||
mDnsup.thread = NS_GetCurrentThread();
|
|
||||||
|
|
||||||
if (!mDnsup.serv) {
|
|
||||||
mDnsup.serv = do_GetService("@mozilla.org/network/dns-service;1", &rv);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
mDnsup.cb = nullptr;
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mDnsup.serv->AsyncResolve(aHost, 0, this, mDnsup.thread, getter_AddRefs(mDnsup.mCancel));
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
Dashboard::OnLookupComplete(nsICancelable *aRequest, nsIDNSRecord *aRecord, nsresult aStatus)
|
|
||||||
{
|
|
||||||
AutoSafeJSContext cx;
|
|
||||||
|
|
||||||
mozilla::dom::DNSLookupDict dict;
|
|
||||||
dict.mAddress.Construct();
|
|
||||||
dict.mError.Construct();
|
|
||||||
dict.mAnswer.Construct();
|
|
||||||
|
|
||||||
Sequence<nsString> &addresses = dict.mAddress.Value();
|
|
||||||
nsString &error = dict.mError.Value();
|
|
||||||
bool &answer = dict.mAnswer.Value();
|
|
||||||
|
|
||||||
if (!NS_FAILED(aStatus)) {
|
|
||||||
answer = true;
|
|
||||||
bool hasMore;
|
|
||||||
aRecord->HasMore(&hasMore);
|
|
||||||
while(hasMore) {
|
|
||||||
nsCString nextAddress;
|
|
||||||
aRecord->GetNextAddrAsString(nextAddress);
|
|
||||||
CopyASCIItoUTF16(nextAddress, *addresses.AppendElement());
|
|
||||||
aRecord->HasMore(&hasMore);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
answer = false;
|
|
||||||
CopyASCIItoUTF16(GetErrorString(aStatus), error);
|
|
||||||
}
|
|
||||||
|
|
||||||
JS::RootedValue val(cx);
|
|
||||||
if (!dict.ToObject(cx, JS::NullPtr(), &val)) {
|
|
||||||
mDnsup.cb = nullptr;
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
mDnsup.cb->OnDashboardDataAvailable(val);
|
|
||||||
mDnsup.cb = nullptr;
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Dashboard::GetDnsInfoDispatch()
|
Dashboard::GetDnsInfoDispatch()
|
||||||
{
|
{
|
||||||
@ -530,6 +472,72 @@ Dashboard::GetDNSCacheEntries()
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
Dashboard::RequestDNSLookup(const nsACString &aHost, NetDashboardCallback *cb)
|
||||||
|
{
|
||||||
|
if (mDnsup.cb)
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
if (!mDnsup.serv) {
|
||||||
|
mDnsup.serv = do_GetService("@mozilla.org/network/dns-service;1", &rv);
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDnsup.cb = cb;
|
||||||
|
rv = mDnsup.serv->AsyncResolve(aHost, 0, this, NS_GetCurrentThread(), getter_AddRefs(mDnsup.cancel));
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mDnsup.cb = nullptr;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
Dashboard::OnLookupComplete(nsICancelable *aRequest, nsIDNSRecord *aRecord, nsresult aStatus)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aRequest == mDnsup.cancel);
|
||||||
|
mDnsup.cancel = nullptr;
|
||||||
|
|
||||||
|
AutoSafeJSContext cx;
|
||||||
|
|
||||||
|
mozilla::dom::DNSLookupDict dict;
|
||||||
|
dict.mAddress.Construct();
|
||||||
|
dict.mError.Construct();
|
||||||
|
dict.mAnswer.Construct();
|
||||||
|
|
||||||
|
Sequence<nsString> &addresses = dict.mAddress.Value();
|
||||||
|
nsString &error = dict.mError.Value();
|
||||||
|
bool &answer = dict.mAnswer.Value();
|
||||||
|
|
||||||
|
if (NS_SUCCEEDED(aStatus)) {
|
||||||
|
answer = true;
|
||||||
|
bool hasMore;
|
||||||
|
aRecord->HasMore(&hasMore);
|
||||||
|
while(hasMore) {
|
||||||
|
nsCString nextAddress;
|
||||||
|
aRecord->GetNextAddrAsString(nextAddress);
|
||||||
|
CopyASCIItoUTF16(nextAddress, *addresses.AppendElement());
|
||||||
|
aRecord->HasMore(&hasMore);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
answer = false;
|
||||||
|
CopyASCIItoUTF16(GetErrorString(aStatus), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::RootedValue val(cx);
|
||||||
|
if (!dict.ToObject(cx, JS::NullPtr(), &val)) {
|
||||||
|
mDnsup.cb = nullptr;
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
mDnsup.cb->OnDashboardDataAvailable(val);
|
||||||
|
mDnsup.cb = nullptr;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
HttpConnInfo::SetHTTP1ProtocolVersion(uint8_t pv)
|
HttpConnInfo::SetHTTP1ProtocolVersion(uint8_t pv)
|
||||||
{
|
{
|
||||||
|
@ -125,9 +125,8 @@ private:
|
|||||||
struct DnsLookup
|
struct DnsLookup
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIDNSService> serv;
|
nsCOMPtr<nsIDNSService> serv;
|
||||||
nsCOMPtr<nsICancelable> mCancel;
|
nsCOMPtr<nsICancelable> cancel;
|
||||||
nsCOMPtr<NetDashboardCallback> cb;
|
nsCOMPtr<NetDashboardCallback> cb;
|
||||||
nsIThread* thread;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConnectionData
|
struct ConnectionData
|
||||||
|
@ -792,9 +792,10 @@ Connection::setClosedState()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Connection::isAsyncClosing() {
|
Connection::isClosing(bool aResultOnClosed) {
|
||||||
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
||||||
return mAsyncExecutionThreadShuttingDown && ConnectionReady();
|
return mAsyncExecutionThreadShuttingDown &&
|
||||||
|
(aResultOnClosed || ConnectionReady());
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@ -842,7 +843,7 @@ Connection::internalClose()
|
|||||||
stmt));
|
stmt));
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
char *msg = ::PR_smprintf("SQL statement '%s' (%x) should have been finalized",
|
char *msg = ::PR_smprintf("SQL statement '%s' (%x) should have been finalized before closing the connection",
|
||||||
::sqlite3_sql(stmt),
|
::sqlite3_sql(stmt),
|
||||||
stmt);
|
stmt);
|
||||||
NS_WARNING(msg);
|
NS_WARNING(msg);
|
||||||
|
@ -167,11 +167,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this is an async connection, it is shutting down and it is not
|
* True if this connection is currently shutting down.
|
||||||
* closed yet.
|
*
|
||||||
|
* In particular, if |isClosing(true)| returns |true|, any sqlite3 statement
|
||||||
|
* belonging to this connection must be discarded as its memory has already
|
||||||
|
* been released to sqlite3.
|
||||||
|
*
|
||||||
|
* @param aResultOnceClosed
|
||||||
|
* The value to return if closing has completed.
|
||||||
*/
|
*/
|
||||||
bool isAsyncClosing();
|
bool isClosing(bool aResultOnceClosed = false);
|
||||||
|
|
||||||
|
|
||||||
nsresult initializeClone(Connection *aClone, bool aReadOnly);
|
nsresult initializeClone(Connection *aClone, bool aReadOnly);
|
||||||
|
|
||||||
@ -239,11 +244,19 @@ private:
|
|||||||
* field.
|
* field.
|
||||||
*/
|
*/
|
||||||
nsCOMPtr<nsIThread> mAsyncExecutionThread;
|
nsCOMPtr<nsIThread> mAsyncExecutionThread;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true by Close() prior to actually shutting down the thread. This
|
* Set to true by Close() or AsyncClose() prior to shutdown.
|
||||||
* lets getAsyncExecutionTarget() know not to hand out any more thread
|
*
|
||||||
* references (or to create the thread in the first place). This variable
|
* If false, we guarantee both that the underlying sqlite3 database
|
||||||
* should be accessed while holding the mAsyncExecutionMutex.
|
* connection is still open and that getAsyncExecutionTarget() can
|
||||||
|
* return a thread. Once true, either the sqlite3 database
|
||||||
|
* connection is being shutdown or it has been
|
||||||
|
* shutdown. Additionally, once true, getAsyncExecutionTarget()
|
||||||
|
* returns null.
|
||||||
|
*
|
||||||
|
* This variable should be accessed while holding the
|
||||||
|
* mAsyncExecutionMutex.
|
||||||
*/
|
*/
|
||||||
bool mAsyncExecutionThreadShuttingDown;
|
bool mAsyncExecutionThreadShuttingDown;
|
||||||
|
|
||||||
|
@ -902,7 +902,7 @@ Service::Observe(nsISupports *, const char *aTopic, const PRUnichar *)
|
|||||||
|
|
||||||
// While it would be nice to close all connections, we only
|
// While it would be nice to close all connections, we only
|
||||||
// check async ones for now.
|
// check async ones for now.
|
||||||
if (conn->isAsyncClosing()) {
|
if (conn->isClosing()) {
|
||||||
anyOpen = true;
|
anyOpen = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -357,12 +357,63 @@ Statement::internalFinalize(bool aDestructing)
|
|||||||
if (!mDBStatement)
|
if (!mDBStatement)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
#ifdef PR_LOGGING
|
int srv = SQLITE_OK;
|
||||||
PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s'",
|
|
||||||
::sqlite3_sql(mDBStatement)));
|
if (!mDBConnection->isClosing(true)) {
|
||||||
#endif
|
//
|
||||||
|
// The connection is still open. While statement finalization and
|
||||||
|
// closing may, in some cases, take place in two distinct threads,
|
||||||
|
// we have a guarantee that the connection will remain open until
|
||||||
|
// this method terminates:
|
||||||
|
//
|
||||||
|
// a. The connection will be closed synchronously. In this case,
|
||||||
|
// there is no race condition, as everything takes place on the
|
||||||
|
// same thread.
|
||||||
|
//
|
||||||
|
// b. The connection is closed asynchronously and this code is
|
||||||
|
// executed on the opener thread. In this case, asyncClose() has
|
||||||
|
// not been called yet and will not be called before we return
|
||||||
|
// from this function.
|
||||||
|
//
|
||||||
|
// c. The connection is closed asynchronously and this code is
|
||||||
|
// executed on the async execution thread. In this case,
|
||||||
|
// AsyncCloseConnection::Run() has not been called yet and will
|
||||||
|
// not be called before we return from this function.
|
||||||
|
//
|
||||||
|
// In either case, the connection is still valid, hence closing
|
||||||
|
// here is safe.
|
||||||
|
//
|
||||||
|
#ifdef PR_LOGGING
|
||||||
|
PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s' during garbage-collection",
|
||||||
|
::sqlite3_sql(mDBStatement)));
|
||||||
|
#endif
|
||||||
|
srv = ::sqlite3_finalize(mDBStatement);
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
else {
|
||||||
|
//
|
||||||
|
// The database connection is either closed or closing. The sqlite
|
||||||
|
// statement has either been finalized already by the connection
|
||||||
|
// or is about to be finalized by the connection.
|
||||||
|
//
|
||||||
|
// Finalizing it here would be useless and segfaultish.
|
||||||
|
//
|
||||||
|
|
||||||
|
char *msg = ::PR_smprintf("SQL statement (%x) should have been finalized"
|
||||||
|
"before garbage-collection. For more details on this statement, set"
|
||||||
|
"NSPR_LOG_MESSAGES=mozStorage:5 .",
|
||||||
|
mDBStatement);
|
||||||
|
//
|
||||||
|
// Note that we can't display the statement itself, as the data structure
|
||||||
|
// is not valid anymore. However, the address shown here should help
|
||||||
|
// developers correlate with the more complete debug message triggered
|
||||||
|
// by AsyncClose().
|
||||||
|
//
|
||||||
|
NS_WARNING(msg);
|
||||||
|
::PR_smprintf_free(msg);
|
||||||
|
}
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
int srv = ::sqlite3_finalize(mDBStatement);
|
|
||||||
mDBStatement = nullptr;
|
mDBStatement = nullptr;
|
||||||
|
|
||||||
if (mAsyncStatement) {
|
if (mAsyncStatement) {
|
||||||
|
@ -294,6 +294,32 @@ add_task(function test_asyncClose_succeeds_with_finalized_async_statement()
|
|||||||
gDBConn = null;
|
gDBConn = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_task(function test_close_then_release_statement() {
|
||||||
|
// Testing the behavior in presence of a bad client that finalizes
|
||||||
|
// statements after the database has been closed (typically by
|
||||||
|
// letting the gc finalize the statement).
|
||||||
|
let db = getOpenedDatabase();
|
||||||
|
let stmt = createStatement("SELECT * FROM test -- test_close_then_release_statement");
|
||||||
|
db.close();
|
||||||
|
stmt.finalize(); // Finalize too late - this should not crash
|
||||||
|
|
||||||
|
// Reset gDBConn so that later tests will get a new connection object.
|
||||||
|
gDBConn = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function test_asyncClose_then_release_statement() {
|
||||||
|
// Testing the behavior in presence of a bad client that finalizes
|
||||||
|
// statements after the database has been async closed (typically by
|
||||||
|
// letting the gc finalize the statement).
|
||||||
|
let db = getOpenedDatabase();
|
||||||
|
let stmt = createStatement("SELECT * FROM test -- test_asyncClose_then_release_statement");
|
||||||
|
yield asyncClose(db);
|
||||||
|
stmt.finalize(); // Finalize too late - this should not crash
|
||||||
|
|
||||||
|
// Reset gDBConn so that later tests will get a new connection object.
|
||||||
|
gDBConn = null;
|
||||||
|
});
|
||||||
|
|
||||||
add_task(function test_close_fails_with_async_statement_ran()
|
add_task(function test_close_fails_with_async_statement_ran()
|
||||||
{
|
{
|
||||||
let deferred = Promise.defer();
|
let deferred = Promise.defer();
|
||||||
|
@ -62,14 +62,6 @@ let SocialServiceInternal = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getManifestByOrigin: function(origin) {
|
|
||||||
for (let manifest of SocialServiceInternal.manifests) {
|
|
||||||
if (origin == manifest.origin) {
|
|
||||||
return manifest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
getManifestPrefname: function(origin) {
|
getManifestPrefname: function(origin) {
|
||||||
// Retrieve the prefname for a given origin/manifest.
|
// Retrieve the prefname for a given origin/manifest.
|
||||||
// If no existing pref, return a generated prefname.
|
// If no existing pref, return a generated prefname.
|
||||||
@ -391,7 +383,7 @@ this.SocialService = {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let manifest = SocialServiceInternal.getManifestByOrigin(origin);
|
let manifest = SocialService.getManifestByOrigin(origin);
|
||||||
if (manifest) {
|
if (manifest) {
|
||||||
let addon = new AddonWrapper(manifest);
|
let addon = new AddonWrapper(manifest);
|
||||||
AddonManagerPrivate.callAddonListeners("onEnabling", addon, false);
|
AddonManagerPrivate.callAddonListeners("onEnabling", addon, false);
|
||||||
@ -416,7 +408,7 @@ this.SocialService = {
|
|||||||
ActiveProviders.add(provider.origin);
|
ActiveProviders.add(provider.origin);
|
||||||
|
|
||||||
this.getOrderedProviderList(function (providers) {
|
this.getOrderedProviderList(function (providers) {
|
||||||
this._notifyProviderListeners("provider-added", providers);
|
this._notifyProviderListeners("provider-enabled", provider.origin, providers);
|
||||||
if (onDone)
|
if (onDone)
|
||||||
onDone(provider);
|
onDone(provider);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
@ -429,7 +421,7 @@ this.SocialService = {
|
|||||||
throw new Error("SocialService.removeProvider: no provider with origin " + origin + " exists!");
|
throw new Error("SocialService.removeProvider: no provider with origin " + origin + " exists!");
|
||||||
|
|
||||||
let provider = SocialServiceInternal.providers[origin];
|
let provider = SocialServiceInternal.providers[origin];
|
||||||
let manifest = SocialServiceInternal.getManifestByOrigin(origin);
|
let manifest = SocialService.getManifestByOrigin(origin);
|
||||||
let addon = manifest && new AddonWrapper(manifest);
|
let addon = manifest && new AddonWrapper(manifest);
|
||||||
if (addon) {
|
if (addon) {
|
||||||
AddonManagerPrivate.callAddonListeners("onDisabling", addon, false);
|
AddonManagerPrivate.callAddonListeners("onDisabling", addon, false);
|
||||||
@ -450,7 +442,7 @@ this.SocialService = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.getOrderedProviderList(function (providers) {
|
this.getOrderedProviderList(function (providers) {
|
||||||
this._notifyProviderListeners("provider-removed", providers);
|
this._notifyProviderListeners("provider-disabled", origin, providers);
|
||||||
if (onDone)
|
if (onDone)
|
||||||
onDone();
|
onDone();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
@ -471,6 +463,15 @@ this.SocialService = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getManifestByOrigin: function(origin) {
|
||||||
|
for (let manifest of SocialServiceInternal.manifests) {
|
||||||
|
if (origin == manifest.origin) {
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
// Returns an array of installed providers, sorted by frecency
|
// Returns an array of installed providers, sorted by frecency
|
||||||
getOrderedProviderList: function(onDone) {
|
getOrderedProviderList: function(onDone) {
|
||||||
SocialServiceInternal.orderedProviders(onDone);
|
SocialServiceInternal.orderedProviders(onDone);
|
||||||
@ -488,10 +489,10 @@ this.SocialService = {
|
|||||||
this._providerListeners.delete(listener);
|
this._providerListeners.delete(listener);
|
||||||
},
|
},
|
||||||
|
|
||||||
_notifyProviderListeners: function (topic, data) {
|
_notifyProviderListeners: function (topic, origin, providers) {
|
||||||
for (let [listener, ] of this._providerListeners) {
|
for (let [listener, ] of this._providerListeners) {
|
||||||
try {
|
try {
|
||||||
listener(topic, data);
|
listener(topic, origin, providers);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Components.utils.reportError("SocialService: provider listener threw an exception: " + ex);
|
Components.utils.reportError("SocialService: provider listener threw an exception: " + ex);
|
||||||
}
|
}
|
||||||
@ -499,7 +500,7 @@ this.SocialService = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_manifestFromData: function(type, data, principal) {
|
_manifestFromData: function(type, data, principal) {
|
||||||
let sameOriginRequired = ['workerURL', 'sidebarURL', 'shareURL'];
|
let sameOriginRequired = ['workerURL', 'sidebarURL', 'shareURL', 'statusURL'];
|
||||||
|
|
||||||
if (type == 'directory') {
|
if (type == 'directory') {
|
||||||
// directory provided manifests must have origin in manifest, use that
|
// directory provided manifests must have origin in manifest, use that
|
||||||
@ -517,8 +518,9 @@ this.SocialService = {
|
|||||||
// iconURL and name are required
|
// iconURL and name are required
|
||||||
// iconURL may be a different origin (CDN or data url support) if this is
|
// iconURL may be a different origin (CDN or data url support) if this is
|
||||||
// a whitelisted or directory listed provider
|
// a whitelisted or directory listed provider
|
||||||
if (!data['workerURL'] && !data['sidebarURL'] && !data['shareURL']) {
|
let providerHasFeatures = [url for (url of sameOriginRequired) if (data[url])].length > 0;
|
||||||
Cu.reportError("SocialService.manifestFromData manifest missing required workerURL or sidebarURL.");
|
if (!providerHasFeatures) {
|
||||||
|
Cu.reportError("SocialService.manifestFromData manifest missing required urls.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!data['name'] || !data['iconURL']) {
|
if (!data['name'] || !data['iconURL']) {
|
||||||
@ -596,7 +598,10 @@ this.SocialService = {
|
|||||||
aAddon.userDisabled = false;
|
aAddon.userDisabled = false;
|
||||||
}
|
}
|
||||||
schedule(function () {
|
schedule(function () {
|
||||||
this._installProvider(aDOMDocument, data, installCallback);
|
this._installProvider(aDOMDocument, data, aManifest => {
|
||||||
|
this._notifyProviderListeners("provider-installed", aManifest.origin);
|
||||||
|
installCallback(aManifest);
|
||||||
|
});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
@ -661,7 +666,7 @@ this.SocialService = {
|
|||||||
* have knowledge of the currently selected provider here, we will notify
|
* have knowledge of the currently selected provider here, we will notify
|
||||||
* the front end to deal with any reload.
|
* the front end to deal with any reload.
|
||||||
*/
|
*/
|
||||||
updateProvider: function(aUpdateOrigin, aManifest, aCallback) {
|
updateProvider: function(aUpdateOrigin, aManifest) {
|
||||||
let originUri = Services.io.newURI(aUpdateOrigin, null, null);
|
let originUri = Services.io.newURI(aUpdateOrigin, null, null);
|
||||||
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
|
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
|
||||||
let installType = this.getOriginActivationType(aUpdateOrigin);
|
let installType = this.getOriginActivationType(aUpdateOrigin);
|
||||||
@ -682,13 +687,15 @@ this.SocialService = {
|
|||||||
let provider = new SocialProvider(manifest);
|
let provider = new SocialProvider(manifest);
|
||||||
SocialServiceInternal.providers[provider.origin] = provider;
|
SocialServiceInternal.providers[provider.origin] = provider;
|
||||||
// update the cache and ui, reload provider if necessary
|
// update the cache and ui, reload provider if necessary
|
||||||
this._notifyProviderListeners("provider-update", provider);
|
this.getOrderedProviderList(providers => {
|
||||||
|
this._notifyProviderListeners("provider-update", provider.origin, providers);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
uninstallProvider: function(origin, aCallback) {
|
uninstallProvider: function(origin, aCallback) {
|
||||||
let manifest = SocialServiceInternal.getManifestByOrigin(origin);
|
let manifest = SocialService.getManifestByOrigin(origin);
|
||||||
let addon = new AddonWrapper(manifest);
|
let addon = new AddonWrapper(manifest);
|
||||||
addon.uninstall(aCallback);
|
addon.uninstall(aCallback);
|
||||||
}
|
}
|
||||||
@ -720,6 +727,7 @@ function SocialProvider(input) {
|
|||||||
this.workerURL = input.workerURL;
|
this.workerURL = input.workerURL;
|
||||||
this.sidebarURL = input.sidebarURL;
|
this.sidebarURL = input.sidebarURL;
|
||||||
this.shareURL = input.shareURL;
|
this.shareURL = input.shareURL;
|
||||||
|
this.statusURL = input.statusURL;
|
||||||
this.origin = input.origin;
|
this.origin = input.origin;
|
||||||
let originUri = Services.io.newURI(input.origin, null, null);
|
let originUri = Services.io.newURI(input.origin, null, null);
|
||||||
this.principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
|
this.principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
|
||||||
@ -766,7 +774,7 @@ SocialProvider.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
get manifest() {
|
get manifest() {
|
||||||
return SocialServiceInternal.getManifestByOrigin(this.origin);
|
return SocialService.getManifestByOrigin(this.origin);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Reference to a workerAPI object for this provider. Null if the provider has
|
// Reference to a workerAPI object for this provider. Null if the provider has
|
||||||
@ -1012,7 +1020,7 @@ function getPrefnameFromOrigin(origin) {
|
|||||||
function AddonInstaller(sourceURI, aManifest, installCallback) {
|
function AddonInstaller(sourceURI, aManifest, installCallback) {
|
||||||
aManifest.updateDate = Date.now();
|
aManifest.updateDate = Date.now();
|
||||||
// get the existing manifest for installDate
|
// get the existing manifest for installDate
|
||||||
let manifest = SocialServiceInternal.getManifestByOrigin(aManifest.origin);
|
let manifest = SocialService.getManifestByOrigin(aManifest.origin);
|
||||||
let isNewInstall = !manifest;
|
let isNewInstall = !manifest;
|
||||||
if (manifest && manifest.installDate)
|
if (manifest && manifest.installDate)
|
||||||
aManifest.installDate = manifest.installDate;
|
aManifest.installDate = manifest.installDate;
|
||||||
@ -1088,6 +1096,7 @@ var SocialAddonProvider = {
|
|||||||
Services.prefs.clearUserPref(getPrefnameFromOrigin(aAddon.manifest.origin));
|
Services.prefs.clearUserPref(getPrefnameFromOrigin(aAddon.manifest.origin));
|
||||||
aAddon.pendingOperations -= AddonManager.PENDING_UNINSTALL;
|
aAddon.pendingOperations -= AddonManager.PENDING_UNINSTALL;
|
||||||
AddonManagerPrivate.callAddonListeners("onUninstalled", aAddon);
|
AddonManagerPrivate.callAddonListeners("onUninstalled", aAddon);
|
||||||
|
SocialService._notifyProviderListeners("provider-uninstalled", aAddon.manifest.origin);
|
||||||
if (aCallback)
|
if (aCallback)
|
||||||
schedule(aCallback);
|
schedule(aCallback);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@ loader.lazyGetter(this, "DOMUtils", () => Cc["@mozilla.org/inspector/dom-utils;1
|
|||||||
const ELEMENT_STYLE = 100;
|
const ELEMENT_STYLE = 100;
|
||||||
exports.ELEMENT_STYLE = ELEMENT_STYLE;
|
exports.ELEMENT_STYLE = ELEMENT_STYLE;
|
||||||
|
|
||||||
|
const PSEUDO_ELEMENTS = [":first-line", ":first-letter", ":before", ":after", ":-moz-selection"];
|
||||||
|
exports.PSEUDO_ELEMENTS = PSEUDO_ELEMENTS;
|
||||||
|
|
||||||
// Predeclare the domnode actor type for use in requests.
|
// Predeclare the domnode actor type for use in requests.
|
||||||
types.addActorType("domnode");
|
types.addActorType("domnode");
|
||||||
|
|
||||||
@ -360,36 +363,46 @@ var PageStyleActor = protocol.ActorClass({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the styles that apply to the element.
|
let pseudoElements = inherited ? [null] : [null, ...PSEUDO_ELEMENTS];
|
||||||
let domRules = DOMUtils.getCSSStyleRules(element);
|
for (let pseudo of pseudoElements) {
|
||||||
|
|
||||||
// getCSSStyleRules returns ordered from least-specific to
|
// Get the styles that apply to the element.
|
||||||
// most-specific.
|
let domRules = DOMUtils.getCSSStyleRules(element, pseudo);
|
||||||
for (let i = domRules.Count() - 1; i >= 0; i--) {
|
|
||||||
let domRule = domRules.GetElementAt(i);
|
|
||||||
|
|
||||||
let isSystem = !CssLogic.isContentStylesheet(domRule.parentStyleSheet);
|
if (!domRules) {
|
||||||
|
|
||||||
if (isSystem && options.filter != CssLogic.FILTER.UA) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inherited) {
|
// getCSSStyleRules returns ordered from least-specific to
|
||||||
// Don't include inherited rules if none of its properties
|
// most-specific.
|
||||||
// are inheritable.
|
for (let i = domRules.Count() - 1; i >= 0; i--) {
|
||||||
let hasInherited = Array.prototype.some.call(domRule.style, prop => {
|
let domRule = domRules.GetElementAt(i);
|
||||||
return DOMUtils.isInheritedProperty(prop);
|
|
||||||
});
|
let isSystem = !CssLogic.isContentStylesheet(domRule.parentStyleSheet);
|
||||||
if (!hasInherited) {
|
|
||||||
|
if (isSystem && options.filter != CssLogic.FILTER.UA) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inherited) {
|
||||||
|
// Don't include inherited rules if none of its properties
|
||||||
|
// are inheritable.
|
||||||
|
let hasInherited = Array.prototype.some.call(domRule.style, prop => {
|
||||||
|
return DOMUtils.isInheritedProperty(prop);
|
||||||
|
});
|
||||||
|
if (!hasInherited) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ruleActor = this._styleRef(domRule);
|
||||||
|
rules.push({
|
||||||
|
rule: ruleActor,
|
||||||
|
inherited: inherited,
|
||||||
|
pseudoElement: pseudo
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let ruleActor = this._styleRef(domRule);
|
|
||||||
rules.push({
|
|
||||||
rule: ruleActor,
|
|
||||||
inherited: inherited,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1658,16 +1658,18 @@ AndroidBridge::SetURITitle(const nsAString& aURI, const nsAString& aTitle)
|
|||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
AndroidBridge::GetSegmentInfoForText(const nsAString& aText,
|
AndroidBridge::GetSegmentInfoForText(const nsAString& aText,
|
||||||
dom::mobilemessage::SmsSegmentInfoData* aData)
|
nsIMobileMessageCallback* aRequest)
|
||||||
{
|
{
|
||||||
#ifndef MOZ_WEBSMS_BACKEND
|
#ifndef MOZ_WEBSMS_BACKEND
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
#else
|
#else
|
||||||
ALOG_BRIDGE("AndroidBridge::GetSegmentInfoForText");
|
ALOG_BRIDGE("AndroidBridge::GetSegmentInfoForText");
|
||||||
|
|
||||||
aData->segments() = 0;
|
dom::mobilemessage::SmsSegmentInfoData data;
|
||||||
aData->charsPerSegment() = 0;
|
|
||||||
aData->charsAvailableInLastSegment() = 0;
|
data.segments() = 0;
|
||||||
|
data.charsPerSegment() = 0;
|
||||||
|
data.charsAvailableInLastSegment() = 0;
|
||||||
|
|
||||||
JNIEnv *env = GetJNIEnv();
|
JNIEnv *env = GetJNIEnv();
|
||||||
if (!env)
|
if (!env)
|
||||||
@ -1686,13 +1688,17 @@ AndroidBridge::GetSegmentInfoForText(const nsAString& aText,
|
|||||||
|
|
||||||
jint* info = env->GetIntArrayElements(arr, JNI_FALSE);
|
jint* info = env->GetIntArrayElements(arr, JNI_FALSE);
|
||||||
|
|
||||||
aData->segments() = info[0]; // msgCount
|
data.segments() = info[0]; // msgCount
|
||||||
aData->charsPerSegment() = info[2]; // codeUnitsRemaining
|
data.charsPerSegment() = info[2]; // codeUnitsRemaining
|
||||||
// segmentChars = (codeUnitCount + codeUnitsRemaining) / msgCount
|
// segmentChars = (codeUnitCount + codeUnitsRemaining) / msgCount
|
||||||
aData->charsAvailableInLastSegment() = (info[1] + info[2]) / info[0];
|
data.charsAvailableInLastSegment() = (info[1] + info[2]) / info[0];
|
||||||
|
|
||||||
env->ReleaseIntArrayElements(arr, info, JNI_ABORT);
|
env->ReleaseIntArrayElements(arr, info, JNI_ABORT);
|
||||||
return NS_OK;
|
|
||||||
|
// TODO Bug 908598 - Should properly use |QueueSmsRequest(...)| to queue up
|
||||||
|
// the nsIMobileMessageCallback just like other functions.
|
||||||
|
nsCOMPtr<nsIDOMMozSmsSegmentInfo> info = new SmsSegmentInfo(data);
|
||||||
|
return aRequest->NotifySegmentInfoForTextGot(info);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +356,7 @@ public:
|
|||||||
void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
|
void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
|
||||||
|
|
||||||
nsresult GetSegmentInfoForText(const nsAString& aText,
|
nsresult GetSegmentInfoForText(const nsAString& aText,
|
||||||
dom::mobilemessage::SmsSegmentInfoData* aData);
|
nsIMobileMessageCallback* aRequest);
|
||||||
void SendMessage(const nsAString& aNumber, const nsAString& aText,
|
void SendMessage(const nsAString& aNumber, const nsAString& aText,
|
||||||
nsIMobileMessageCallback* aRequest);
|
nsIMobileMessageCallback* aRequest);
|
||||||
void GetMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest);
|
void GetMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest);
|
||||||
|
Loading…
Reference in New Issue
Block a user