Merge m-c to b2g-inbound a=merge

This commit is contained in:
Wes Kocher 2015-02-25 17:52:48 -08:00
commit cab7aa8023
306 changed files with 22431 additions and 18599 deletions

View File

@ -330,12 +330,17 @@ static gint
getCharacterCountCB(AtkText *aText)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (!accWrap)
return 0;
if (accWrap) {
HyperTextAccessible* textAcc = accWrap->AsHyperText();
return
textAcc->IsDefunct() ? 0 : static_cast<gint>(textAcc->CharacterCount());
}
HyperTextAccessible* textAcc = accWrap->AsHyperText();
return textAcc->IsDefunct() ?
0 : static_cast<gint>(textAcc->CharacterCount());
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->CharacterCount();
}
return 0;
}
static gint
@ -362,14 +367,20 @@ static gint
getTextSelectionCountCB(AtkText *aText)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (!accWrap)
return 0;
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return 0;
}
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole())
return 0;
return text->SelectionCount();
}
return text->SelectionCount();
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->SelectionCount();
}
return 0;
}
static gchar*

View File

@ -207,6 +207,22 @@ DocAccessibleChild::RecvRelations(const uint64_t& aID,
return true;
}
bool
DocAccessibleChild::RecvCharacterCount(const uint64_t& aID, int32_t* aCount)
{
HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
*aCount = acc ? acc->CharacterCount() : 0;
return true;
}
bool
DocAccessibleChild::RecvSelectionCount(const uint64_t& aID, int32_t* aCount)
{
HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
*aCount = acc ? acc->SelectionCount() : 0;
return true;
}
bool
DocAccessibleChild::RecvTextSubstring(const uint64_t& aID,
const int32_t& aStartOffset,

View File

@ -63,6 +63,12 @@ public:
virtual bool RecvAttributes(const uint64_t& aID,
nsTArray<Attribute> *aAttributes) MOZ_OVERRIDE;
virtual bool RecvCharacterCount(const uint64_t& aID, int32_t* aCount)
MOZ_OVERRIDE;
virtual bool RecvSelectionCount(const uint64_t& aID, int32_t* aCount)
MOZ_OVERRIDE;
virtual bool RecvTextSubstring(const uint64_t& aID,
const int32_t& aStartOffset,
const int32_t& aEndOffset, nsString* aText)

View File

@ -65,6 +65,8 @@ child:
// AccessibleText
// TextSubstring is getText in IDL.
prio(high) sync CharacterCount(uint64_t aID) returns(int32_t aCount);
prio(high) sync SelectionCount(uint64_t aID) returns(int32_t aCount);
prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
aEndOffset) returns(nsString aText);
prio(high) sync GetTextAfterOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)

View File

@ -149,6 +149,22 @@ ProxyAccessible::Relations(nsTArray<RelationType>* aTypes,
}
}
int32_t
ProxyAccessible::CharacterCount()
{
int32_t count = 0;
unused << mDoc->SendCharacterCount(mID, &count);
return count;
}
int32_t
ProxyAccessible::SelectionCount()
{
int32_t count = 0;
unused << mDoc->SendSelectionCount(mID, &count);
return count;
}
void
ProxyAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOfset,
nsString& aText) const

View File

@ -99,6 +99,9 @@ public:
void Relations(nsTArray<RelationType>* aTypes,
nsTArray<nsTArray<ProxyAccessible*>>* aTargetSets) const;
int32_t CharacterCount();
int32_t SelectionCount();
/**
* Get the text between the given offsets.
*/

View File

@ -1226,8 +1226,8 @@ pref("security.sandbox.windows.log.stackTraceDepth", 0);
// 2 -> "an ideal sandbox which may break many things"
// This setting is read when the content process is started. On Mac the content
// process is killed when all windows are closed, so a change will take effect
// when the 1st window is opened. It was decided to default this setting to 1.
pref("security.sandbox.macos.content.level", 1);
// when the 1st window is opened.
pref("security.sandbox.macos.content.level", 0);
#endif
// This pref governs whether we attempt to work around problems caused by

View File

@ -32,11 +32,7 @@ let SidebarUI = {
this._title = document.getElementById("sidebar-title");
this._splitter = document.getElementById("sidebar-splitter");
if (window.opener && !window.opener.closed &&
window.opener.document.documentURIObject.schemeIs("chrome") &&
PrivateBrowsingUtils.isWindowPrivate(window) == PrivateBrowsingUtils.isWindowPrivate(window.opener)) {
this.adoptFromWindow(window.opener);
} else {
if (!this.adoptFromWindow(window.opener)) {
let commandID = this._box.getAttribute("sidebarcommand");
if (commandID) {
let command = document.getElementById(commandID);
@ -67,24 +63,40 @@ let SidebarUI = {
},
/**
* Adopt the status of the sidebar from another window.
* Try and adopt the status of the sidebar from another window.
* @param {Window} sourceWindow - Window to use as a source for sidebar status.
* @return true if we adopted the state, or false if the caller should
* initialize the state itself.
*/
adoptFromWindow(sourceWindow) {
// No source window, or it being closed, or not chrome, or in a different
// private-browsing context means we can't adopt.
if (!sourceWindow || sourceWindow.closed ||
!sourceWindow.document.documentURIObject.schemeIs("chrome") ||
PrivateBrowsingUtils.isWindowPrivate(window) != PrivateBrowsingUtils.isWindowPrivate(sourceWindow)) {
return false;
}
// If the opener had a sidebar, open the same sidebar in our window.
// The opener can be the hidden window too, if we're coming from the state
// where no windows are open, and the hidden window has no sidebar box.
let sourceUI = sourceWindow.SidebarUI;
if (!sourceUI || sourceUI._box.hidden) {
return;
if (!sourceUI || !sourceUI._box) {
// no source UI or no _box means we also can't adopt the state.
return false;
}
if (sourceUI._box.hidden) {
// just hidden means we have adopted the hidden state.
return true;
}
let commandID = sourceUI._box.getAttribute("sidebarcommand");
let commandElem = document.getElementById(commandID);
// dynamically generated sidebars will fail this check.
// dynamically generated sidebars will fail this check, but we still
// consider it adopted.
if (!commandElem) {
return;
return true;
}
this._title.setAttribute("value",
@ -101,6 +113,7 @@ let SidebarUI = {
this._box.hidden = false;
this._splitter.hidden = false;
commandElem.setAttribute("checked", "true");
return true;
},
/**

View File

@ -16,5 +16,7 @@ FIREFOX_PREFERENCES = {
"loop.seenToS": "seen",
# this dialog is fragile, and likely to introduce intermittent failures
"media.navigator.permission.disabled": True
"media.navigator.permission.disabled": True,
# Use fake streams only
"media.navigator.streams.fake": True
}

View File

@ -1,12 +1,18 @@
from marionette_test import MarionetteTestCase
from by import By
import urlparse
from errors import NoSuchElementException, StaleElementException
# noinspection PyUnresolvedReferences
from wait import Wait
try:
from by import By
from errors import NoSuchElementException, StaleElementException
# noinspection PyUnresolvedReferences
from wait import Wait
except ImportError:
from marionette_driver.by import By
from marionette_driver.errors import NoSuchElementException, StaleElementException
# noinspection PyUnresolvedReferences
from marionette_driver import Wait
import os
import sys
import urlparse
sys.path.insert(1, os.path.dirname(os.path.abspath(__file__)))
import pyperclip

View File

@ -1,5 +1,5 @@
from marionette_test import MarionetteTestCase
from errors import NoSuchElementException
from marionette_driver.errors import NoSuchElementException
import threading
import SimpleHTTPServer
import SocketServer

View File

@ -37,6 +37,7 @@ add_task(function* setup_server() {
add_task(function* error_offline() {
Services.io.offline = true;
Services.prefs.setBoolPref("network.dns.offline-localhost", false);
yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/offline", "GET").then(
() => Assert.ok(false, "Should have rejected"),
(error) => {
@ -179,6 +180,7 @@ function run_test() {
Services.prefs.clearUserPref("loop.hawk-session-token");
Services.prefs.clearUserPref("loop.hawk-session-token.fxa");
Services.prefs.clearUserPref("loop.urlsExpiryTimeSeconds");
Services.prefs.clearUserPref("network.dns.offline-localhost");
MozLoopService.errors.clear();
});

View File

@ -3,12 +3,14 @@
* 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/. */
Components.utils.import("resource://gre/modules/Services.jsm");
const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
const nsICookiePermission = Components.interfaces.nsICookiePermission;
const NOTIFICATION_FLUSH_PERMISSIONS = "flush-pending-permissions";
function Permission(host, rawHost, type, capability)
function Permission(host, rawHost, type, capability)
{
this.host = host;
this.rawHost = rawHost;
@ -17,18 +19,19 @@ function Permission(host, rawHost, type, capability)
}
var gPermissionManager = {
_type : "",
_permissions : [],
_pm : Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager),
_bundle : null,
_tree : null,
_type : "",
_permissions : [],
_permissionsToAdd : new Map(),
_permissionsToDelete : new Map(),
_bundle : null,
_tree : null,
_observerRemoved : false,
_view: {
_rowCount: 0,
get rowCount()
{
return this._rowCount;
get rowCount()
{
return this._rowCount;
},
getCellText: function (aRow, aColumn)
{
@ -56,7 +59,7 @@ var gPermissionManager = {
return "";
}
},
_getCapabilityString: function (aCapability)
{
var stringKey = null;
@ -76,44 +79,47 @@ var gPermissionManager = {
}
return this._bundle.getString(stringKey);
},
addPermission: function (aCapability)
{
var textbox = document.getElementById("url");
var host = textbox.value.replace(/^\s*([-\w]*:\/+)?/, ""); // trim any leading space and scheme
try {
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var uri = ioService.newURI("http://"+host, null, null);
var uri = Services.io.newURI("http://"+host, null, null);
host = uri.host;
} catch(ex) {
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var message = this._bundle.getString("invalidURI");
var title = this._bundle.getString("invalidURITitle");
promptService.alert(window, title, message);
Services.prompt.alert(window, title, message);
return;
}
var capabilityString = this._getCapabilityString(aCapability);
// check whether the permission already exists, if not, add it
var exists = false;
let hostExists = false;
let capabilityExists = false;
for (var i = 0; i < this._permissions.length; ++i) {
if (this._permissions[i].rawHost == host) {
// Avoid calling the permission manager if the capability settings are
// the same. Otherwise allow the call to the permissions manager to
// update the listbox for us.
exists = this._permissions[i].capability == capabilityString;
hostExists = true;
capabilityExists = this._permissions[i].capability == capabilityString;
if (!capabilityExists) {
this._permissions[i].capability = capabilityString;
}
break;
}
}
if (!exists) {
host = (host.charAt(0) == ".") ? host.substring(1,host.length) : host;
var uri = ioService.newURI("http://" + host, null, null);
this._pm.add(uri, this._type, aCapability);
let permissionParams = {host: host, type: this._type, capability: aCapability};
if (!hostExists) {
this._permissionsToAdd.set(host, permissionParams);
this._addPermission(permissionParams);
}
else if (!capabilityExists) {
this._permissionsToAdd.set(host, permissionParams);
this._handleCapabilityChange();
}
textbox.value = "";
textbox.focus();
@ -123,14 +129,57 @@ var gPermissionManager = {
// enable "remove all" button as needed
document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
},
_removePermission: function(aPermission)
{
this._removePermissionFromList(aPermission.host);
// If this permission was added during this session, let's remove
// it from the pending adds list to prevent calls to the
// permission manager.
let isNewPermission = this._permissionsToAdd.delete(aPermission.host);
if (!isNewPermission) {
this._permissionsToDelete.set(aPermission.host, aPermission);
}
},
_handleCapabilityChange: function ()
{
// Re-do the sort, if the status changed from Block to Allow
// or vice versa, since if we're sorted on status, we may no
// longer be in order.
if (this._lastPermissionSortColumn.id == "statusCol") {
this._resortPermissions();
}
this._tree.treeBoxObject.invalidate();
},
_addPermission: function(aPermission)
{
this._addPermissionToList(aPermission);
++this._view._rowCount;
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1);
// Re-do the sort, since we inserted this new item at the end.
this._resortPermissions();
},
_resortPermissions: function()
{
gTreeUtils.sort(this._tree, this._view, this._permissions,
this._permissionsComparator,
this._lastPermissionSortColumn,
this._lastPermissionSortAscending);
},
onHostInput: function (aSiteField)
{
document.getElementById("btnSession").disabled = !aSiteField.value;
document.getElementById("btnBlock").disabled = !aSiteField.value;
document.getElementById("btnAllow").disabled = !aSiteField.value;
},
onWindowKeyPress: function (aEvent)
{
if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
@ -142,14 +191,14 @@ var gPermissionManager = {
if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
document.getElementById("btnAllow").click();
},
onLoad: function ()
{
this._bundle = document.getElementById("bundlePreferences");
var params = window.arguments[0];
this.init(params);
},
init: function (aParams)
{
if (this._type) {
@ -159,14 +208,14 @@ var gPermissionManager = {
this._type = aParams.permissionType;
this._manageCapability = aParams.manageCapability;
var permissionsText = document.getElementById("permissionsText");
while (permissionsText.hasChildNodes())
permissionsText.removeChild(permissionsText.firstChild);
permissionsText.appendChild(document.createTextNode(aParams.introText));
document.title = aParams.windowTitle;
document.getElementById("btnBlock").hidden = !aParams.blockVisible;
document.getElementById("btnSession").hidden = !aParams.sessionVisible;
document.getElementById("btnAllow").hidden = !aParams.allowVisible;
@ -182,23 +231,23 @@ var gPermissionManager = {
var urlLabel = document.getElementById("urlLabel");
urlLabel.hidden = !urlFieldVisible;
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.notifyObservers(null, NOTIFICATION_FLUSH_PERMISSIONS, this._type);
os.addObserver(this, "perm-changed", false);
Services.obs.notifyObservers(null, NOTIFICATION_FLUSH_PERMISSIONS, this._type);
Services.obs.addObserver(this, "perm-changed", false);
this._loadPermissions();
urlField.focus();
},
uninit: function ()
{
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.removeObserver(this, "perm-changed");
if (!this._observerRemoved) {
Services.obs.removeObserver(this, "perm-changed");
this._observerRemoved = true;
}
},
observe: function (aSubject, aTopic, aData)
{
if (aTopic == "perm-changed") {
@ -209,14 +258,7 @@ var gPermissionManager = {
return;
if (aData == "added") {
this._addPermissionToList(permission);
++this._view._rowCount;
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1);
// Re-do the sort, since we inserted this new item at the end.
gTreeUtils.sort(this._tree, this._view, this._permissions,
this._permissionsComparator,
this._lastPermissionSortColumn,
this._lastPermissionSortAscending);
this._addPermission(permission);
}
else if (aData == "changed") {
for (var i = 0; i < this._permissions.length; ++i) {
@ -225,31 +267,14 @@ var gPermissionManager = {
break;
}
}
// Re-do the sort, if the status changed from Block to Allow
// or vice versa, since if we're sorted on status, we may no
// longer be in order.
if (this._lastPermissionSortColumn.id == "statusCol") {
gTreeUtils.sort(this._tree, this._view, this._permissions,
this._permissionsComparator,
this._lastPermissionSortColumn,
this._lastPermissionSortAscending);
}
this._tree.treeBoxObject.invalidate();
this._handleCapabilityChange();
}
else if (aData == "deleted") {
for (var i = 0; i < this._permissions.length; i++) {
if (this._permissions[i].host == permission.host) {
this._permissions.splice(i, 1);
this._view._rowCount--;
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
this._tree.treeBoxObject.invalidate();
break;
}
}
this._removePermissionFromList(permission);
}
}
},
onPermissionSelected: function ()
{
var hasSelection = this._tree.view.selection.count > 0;
@ -257,7 +282,7 @@ var gPermissionManager = {
document.getElementById("removePermission").disabled = !hasRows || !hasSelection;
document.getElementById("removeAllPermissions").disabled = !hasRows;
},
onPermissionDeleted: function ()
{
if (!this._view.rowCount)
@ -266,12 +291,12 @@ var gPermissionManager = {
gTreeUtils.deleteSelectedItems(this._tree, this._view, this._permissions, removedPermissions);
for (var i = 0; i < removedPermissions.length; ++i) {
var p = removedPermissions[i];
this._pm.remove(p.host, p.type);
}
this._removePermission(p);
}
document.getElementById("removePermission").disabled = !this._permissions.length;
document.getElementById("removeAllPermissions").disabled = !this._permissions.length;
},
onAllPermissionsDeleted: function ()
{
if (!this._view.rowCount)
@ -280,12 +305,12 @@ var gPermissionManager = {
gTreeUtils.deleteAll(this._tree, this._view, this._permissions, removedPermissions);
for (var i = 0; i < removedPermissions.length; ++i) {
var p = removedPermissions[i];
this._pm.remove(p.host, p.type);
}
this._removePermission(p);
}
document.getElementById("removePermission").disabled = true;
document.getElementById("removeAllPermissions").disabled = true;
},
onPermissionKeyPress: function (aEvent)
{
if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE
@ -295,7 +320,7 @@ var gPermissionManager = {
)
this.onPermissionDeleted();
},
_lastPermissionSortColumn: "",
_lastPermissionSortAscending: false,
_permissionsComparator : function (a, b)
@ -303,19 +328,38 @@ var gPermissionManager = {
return a.toLowerCase().localeCompare(b.toLowerCase());
},
onPermissionSort: function (aColumn)
{
this._lastPermissionSortAscending = gTreeUtils.sort(this._tree,
this._view,
this._lastPermissionSortAscending = gTreeUtils.sort(this._tree,
this._view,
this._permissions,
aColumn,
this._permissionsComparator,
this._lastPermissionSortColumn,
this._lastPermissionSortColumn,
this._lastPermissionSortAscending);
this._lastPermissionSortColumn = aColumn;
},
onApplyChanges: function()
{
// Stop observing permission changes since we are about
// to write out the pending adds/deletes and don't need
// to update the UI
this.uninit();
for (let permissionParams of this._permissionsToAdd.values()) {
let uri = Services.io.newURI("http://" + permissionParams.host, null, null);
Services.perms.add(uri, permissionParams.type, permissionParams.capability);
}
for (let p of this._permissionsToDelete.values()) {
Services.perms.remove(p.host, p.type);
}
window.close();
},
_loadPermissions: function ()
{
this._tree = document.getElementById("permissionsTree");
@ -323,12 +367,12 @@ var gPermissionManager = {
// load permissions into a table
var count = 0;
var enumerator = this._pm.enumerator;
var enumerator = Services.perms.enumerator;
while (enumerator.hasMoreElements()) {
var nextPermission = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
this._addPermissionToList(nextPermission);
}
this._view._rowCount = this._permissions.length;
// sort and display the table
@ -338,7 +382,7 @@ var gPermissionManager = {
// disable "remove all" button if there are none
document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
},
_addPermissionToList: function (aPermission)
{
if (aPermission.type == this._type &&
@ -352,9 +396,22 @@ var gPermissionManager = {
aPermission.type,
capabilityString);
this._permissions.push(p);
}
}
},
_removePermissionFromList: function (aHost)
{
for (let i = 0; i < this._permissions.length; ++i) {
if (this._permissions[i].host == aHost) {
this._permissions.splice(i, 1);
this._view._rowCount--;
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
this._tree.treeBoxObject.invalidate();
break;
}
}
},
setHost: function (aHost)
{
document.getElementById("url").value = aHost;

View File

@ -61,8 +61,8 @@
<treechildren/>
</tree>
</vbox>
<hbox align="end">
<hbox class="actionButtons" flex="1">
<vbox>
<hbox class="actionButtons" align="left" flex="1">
<button id="removePermission" disabled="true"
accesskey="&removepermission.accesskey;"
icon="remove" label="&removepermission.label;"
@ -71,9 +71,13 @@
icon="clear" label="&removeallpermissions.label;"
accesskey="&removeallpermissions.accesskey;"
oncommand="gPermissionManager.onAllPermissionsDeleted();"/>
<spacer flex="1"/>
<button oncommand="close();" icon="close"
label="&button.close.label;" accesskey="&button.close.accesskey;"/>
</hbox>
</hbox>
<spacer flex="1"/>
<hbox class="actionButtons" align="right" flex="1">
<button oncommand="close();" icon="close"
label="&button.cancel.label;" accesskey="&button.cancel.accesskey;" />
<button id="btnApplyChanges" oncommand="gPermissionManager.onApplyChanges();" icon="save"
label="&button.ok.label;" accesskey="&button.ok.accesskey;"/>
</hbox>
</vbox>
</window>

View File

@ -4,182 +4,197 @@
function test() {
waitForExplicitFinish();
function prefWindowObserver(subject, topic, data) {
if (topic != "domwindowopened")
return;
Services.ww.unregisterNotification(this);
let win = subject.QueryInterface(Ci.nsIDOMEventTarget);
win.addEventListener("load", function(event) {
let historyMode = event.target.getElementById("historyMode");
historyMode.value = "custom";
historyMode.doCommand();
Services.ww.registerNotification(cookiesWindowObserver);
event.target.getElementById("cookieExceptions").doCommand();
}, false);
}
function cookiesWindowObserver(subject, topic, data) {
if (topic != "domwindowopened")
return;
Services.ww.unregisterNotification(this);
let win = subject.QueryInterface(Ci.nsIDOMEventTarget);
win.addEventListener("load", function(event) {
SimpleTest.executeSoon(function() windowLoad(event, win, dialog));
}, false);
}
Services.ww.registerNotification(prefWindowObserver);
let dialog = openDialog("chrome://browser/content/preferences/preferences.xul",
"Preferences", "chrome,titlebar,toolbar,centerscreen,dialog=no",
"panePrivacy");
testRunner.runTests();
}
function windowLoad(event, win, dialog) {
let doc = event.target;
let tree = doc.getElementById("permissionsTree");
let statusCol = tree.treeBoxObject.columns.getColumnAt(1);
let url = doc.getElementById("url");
let btnAllow = doc.getElementById("btnAllow");
let btnBlock = doc.getElementById("btnBlock");
let btnRemove = doc.getElementById("removePermission");
let pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
let ioService = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
const allowText = win.gPermissionManager._getCapabilityString(
Ci.nsIPermissionManager.ALLOW_ACTION);
const denyText = win.gPermissionManager._getCapabilityString(
Ci.nsIPermissionManager.DENY_ACTION);
const allow = Ci.nsIPermissionManager.ALLOW_ACTION;
const deny = Ci.nsIPermissionManager.DENY_ACTION;
var testRunner = {
is(tree.view.rowCount, 0, "no cookie exceptions");
tests:
[
{
test: function(params) {
params.url.value = "test.com";
params.btnAllow.doCommand();
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission text should be set correctly");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", host: "test.com", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
},
{
test: function(params) {
params.url.value = "test.com";
params.btnBlock.doCommand();
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
"permission should change to deny in UI");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", host: "test.com", data: "changed",
capability: Ci.nsIPermissionManager.DENY_ACTION }],
},
{
test: function(params) {
params.url.value = "test.com";
params.btnAllow.doCommand();
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission should revert back to allow");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", host: "test.com", data: "changed",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
},
{
test: function(params) {
params.url.value = "test.com";
params.btnRemove.doCommand();
is(params.tree.view.rowCount, 0, "exception should be removed");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", host: "test.com", data: "deleted" }],
},
{
test: function(params) {
let uri = params.ioService.newURI("http://test.com", null, null);
params.pm.add(uri, "popup", Ci.nsIPermissionManager.DENY_ACTION);
is(params.tree.view.rowCount, 0, "adding unrelated permission should not change display");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "popup", host: "test.com", data: "added",
capability: Ci.nsIPermissionManager.DENY_ACTION }],
cleanUp: function(params) {
params.pm.remove("test.com", "popup");
},
},
],
let tests = [
{
test: function() {
url.value = "test.com";
btnAllow.doCommand();
is(tree.view.rowCount, 1, "added exception shows up in treeview");
is(tree.view.getCellText(0, statusCol), allowText,
"permission text should be set correctly");
},
observances: [{ type: "cookie", host: "test.com", data: "added",
capability: allow }]
},
{
test: function() {
url.value = "test.com";
btnBlock.doCommand();
is(tree.view.getCellText(0, statusCol), denyText,
"permission should change to deny in UI");
},
observances: [{ type: "cookie", host: "test.com", data: "changed",
capability: deny }],
},
{
test: function() {
url.value = "test.com";
btnAllow.doCommand();
is(tree.view.getCellText(0, statusCol), allowText,
"permission should revert back to allow");
},
observances: [{ type: "cookie", host: "test.com", data: "changed",
capability: allow }],
},
{
test: function() {
url.value = "test.com";
btnRemove.doCommand();
is(tree.view.rowCount, 0, "exception should be removed");
},
observances: [{ type: "cookie", host: "test.com", data: "deleted" }],
},
{
test: function() {
let uri = ioService.newURI("http://test.com", null, null);
pm.add(uri, "popup", Ci.nsIPermissionManager.DENY_ACTION);
is(tree.view.rowCount, 0, "adding unrelated permission should not change display");
},
observances: [{ type: "popup", host: "test.com", data: "added",
capability: deny }],
cleanUp: function() {
pm.remove("test.com", "popup");
},
},
{
test: function() {
url.value = "test.com";
btnAllow.doCommand();
pm.remove("test.com", "cookie");
is(tree.view.rowCount, 0, "display should update when cookie permission is deleted");
},
observances: [{ type: "cookie", host: "test.com", data: "added",
capability: allow },
{ type: "cookie", host: "test.com", data: "deleted" }]
},
];
_currentTest: -1,
let permObserver = {
observe: function(aSubject, aTopic, aData) {
if (aTopic != "perm-changed")
return;
runTests: function() {
this._currentTest++;
if (tests[currentTest].observances.length == 0) {
// Should fail here as we are not expecting a notification, but we don't.
// See bug 1063410.
return;
info("Running test #" + (this._currentTest + 1) + "\n");
let that = this;
let p = this.runCurrentTest();
p.then(function() {
if (that._currentTest == that.tests.length - 1) {
finish();
}
let permission = aSubject.QueryInterface(Ci.nsIPermission);
let expected = tests[currentTest].observances.shift();
is(aData, expected.data, "type of message should be the same");
for each (let prop in ["type", "host", "capability"]) {
if (expected[prop])
is(permission[prop], expected[prop],
"property: \"" + prop + "\" should be equal");
else {
that.runTests();
}
});
},
if (tests[currentTest].observances.length == 0) {
SimpleTest.executeSoon(function() {
if (tests[currentTest].cleanUp)
tests[currentTest].cleanUp();
runCurrentTest: function() {
return new Promise(function(resolve, reject) {
runNextTest();
});
}
},
};
let helperFunctions = {
let os = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
prefWindowObserver: function(subject, topic, data) {
if (topic != "domwindowopened")
return;
os.addObserver(permObserver, "perm-changed", false);
Services.ww.unregisterNotification(helperFunctions.prefWindowObserver);
var currentTest = -1;
function runNextTest() {
currentTest++;
if (currentTest == tests.length) {
os.removeObserver(permObserver, "perm-changed");
win.close();
dialog.close();
finish();
return;
}
let win = subject.QueryInterface(Ci.nsIDOMEventTarget);
info("Running test #" + (currentTest + 1) + "\n");
tests[currentTest].test();
if (!tests[currentTest].observances)
runNextTest();
}
win.addEventListener("load", function(event) {
let historyMode = event.target.getElementById("historyMode");
historyMode.value = "custom";
historyMode.doCommand();
Services.ww.registerNotification(helperFunctions.cookiesWindowObserver);
event.target.getElementById("cookieExceptions").doCommand();
}, false);
},
runNextTest();
}
cookiesWindowObserver: function(subject, topic, data) {
if (topic != "domwindowopened")
return;
Services.ww.unregisterNotification(helperFunctions.cookiesWindowObserver);
let win = subject.QueryInterface(Ci.nsIDOMEventTarget);
win.addEventListener("load", function(event) {
SimpleTest.executeSoon(function() helperFunctions.windowLoad(event, win));
}, false);
},
windowLoad: function(event, win) {
let params = {
doc: event.target,
tree: event.target.getElementById("permissionsTree"),
statusCol: event.target.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(1),
url: event.target.getElementById("url"),
btnAllow: event.target.getElementById("btnAllow"),
btnBlock: event.target.getElementById("btnBlock"),
btnApplyChanges: event.target.getElementById("btnApplyChanges"),
btnRemove: event.target.getElementById("removePermission"),
pm: Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager),
ioService: Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService),
allowText: win.gPermissionManager._getCapabilityString(
Ci.nsIPermissionManager.ALLOW_ACTION),
denyText: win.gPermissionManager._getCapabilityString(
Ci.nsIPermissionManager.DENY_ACTION),
allow: Ci.nsIPermissionManager.ALLOW_ACTION,
deny: Ci.nsIPermissionManager.DENY_ACTION,
};
let permObserver = {
observe: function(aSubject, aTopic, aData) {
if (aTopic != "perm-changed")
return;
if (testRunner.tests[testRunner._currentTest].observances.length == 0) {
// Should fail here as we are not expecting a notification, but we don't.
// See bug 1063410.
return;
}
let permission = aSubject.QueryInterface(Ci.nsIPermission);
let expected = testRunner.tests[testRunner._currentTest].observances.shift();
is(aData, expected.data, "type of message should be the same");
for each (let prop in ["type", "host", "capability"]) {
if (expected[prop])
is(permission[prop], expected[prop],
"property: \"" + prop + "\" should be equal");
}
os.removeObserver(permObserver, "perm-changed");
if (testRunner.tests[testRunner._currentTest].cleanup) {
testRunner.tests[testRunner._currentTest].cleanup();
}
testRunner.dialog.close(params);
win.close();
resolve();
},
};
let os = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
os.addObserver(permObserver, "perm-changed", false);
if (testRunner._currentTest == 0) {
is(params.tree.view.rowCount, 0, "no cookie exceptions");
}
testRunner.tests[testRunner._currentTest].test(params);
},
};
Services.ww.registerNotification(helperFunctions.prefWindowObserver);
testRunner.dialog = openDialog("chrome://browser/content/preferences/preferences.xul",
"Preferences", "chrome,titlebar,toolbar,centerscreen,dialog=no",
"panePrivacy");
});
},
};

View File

@ -12,8 +12,9 @@ const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
let XMLHttpRequest = CC("@mozilla.org/xmlextras/xmlhttprequest;1");
let strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties");
function AppValidator(project) {
this.project = project;
function AppValidator({ type, location }) {
this.type = type;
this.location = location;
this.errors = [];
this.warnings = [];
}
@ -27,7 +28,7 @@ AppValidator.prototype.warning = function (message) {
};
AppValidator.prototype._getPackagedManifestFile = function () {
let manifestFile = FileUtils.File(this.project.location);
let manifestFile = FileUtils.File(this.location);
if (!manifestFile.exists()) {
this.error(strings.GetStringFromName("validator.nonExistingFolder"));
return null;
@ -149,12 +150,12 @@ AppValidator.prototype._fetchManifest = function (manifestURL) {
AppValidator.prototype._getManifest = function () {
let manifestURL;
if (this.project.type == "packaged") {
if (this.type == "packaged") {
manifestURL = this._getPackagedManifestURL();
if (!manifestURL)
return promise.resolve(null);
} else if (this.project.type == "hosted") {
manifestURL = this.project.location;
} else if (this.type == "hosted") {
manifestURL = this.location;
try {
Services.io.newURI(manifestURL, null, null);
} catch(e) {
@ -162,7 +163,7 @@ AppValidator.prototype._getManifest = function () {
return promise.resolve(null);
}
} else {
this.error(strings.formatStringFromName("validator.invalidProjectType", [this.project.type], 1));
this.error(strings.formatStringFromName("validator.invalidProjectType", [this.type], 1));
return promise.resolve(null);
}
return this._fetchManifest(manifestURL);
@ -181,11 +182,11 @@ AppValidator.prototype.validateManifest = function (manifest) {
};
AppValidator.prototype._getOriginURL = function () {
if (this.project.type == "packaged") {
if (this.type == "packaged") {
let manifestURL = Services.io.newURI(this.manifestURL, null, null);
return Services.io.newURI(".", null, manifestURL).spec;
} else if (this.project.type == "hosted") {
return Services.io.newURI(this.project.location, null, null).prePath;
} else if (this.type == "hosted") {
return Services.io.newURI(this.location, null, null).prePath;
}
};
@ -203,9 +204,9 @@ AppValidator.prototype.validateLaunchPath = function (manifest) {
}
let origin = this._getOriginURL();
let path;
if (this.project.type == "packaged") {
if (this.type == "packaged") {
path = "." + ( manifest.launch_path || "/index.html" );
} else if (this.project.type == "hosted") {
} else if (this.type == "hosted") {
path = manifest.launch_path || "/";
}
let indexURL;
@ -251,7 +252,7 @@ AppValidator.prototype.validateType = function (manifest) {
let appType = manifest.type || "web";
if (["web", "trusted", "privileged", "certified"].indexOf(appType) === -1) {
this.error(strings.formatStringFromName("validator.invalidAppType", [appType], 1));
} else if (this.project.type == "hosted" &&
} else if (this.type == "hosted" &&
["certified", "privileged"].indexOf(appType) !== -1) {
this.error(strings.formatStringFromName("validator.invalidHostedPriviledges", [appType], 1));
}

View File

@ -109,7 +109,7 @@
let validator = createPackaged("wrong-launch-path");
validator.validate().then(() => {
is(validator.errors.length, 1, "app with wrong path got an error");
let file = nsFile(validator.project.location);
let file = nsFile(validator.location);
file.append("wrong-path.html");
let url = Services.io.newFileURI(file);
is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec], 1),

View File

@ -9,8 +9,8 @@
// Expected values:
let res1 = [
{selector: "#element-size", value: "160" + "\u00D7" + "160"},
{selector: ".size > span", value: "100" + "\u00D7" + "100"},
{selector: "#element-size", value: "160" + "\u00D7" + "160.117"},
{selector: ".size > span", value: "100" + "\u00D7" + "100.117"},
{selector: ".margin.top > span", value: 30},
{selector: ".margin.left > span", value: "auto"},
{selector: ".margin.bottom > span", value: 30},
@ -43,7 +43,7 @@ let res2 = [
];
add_task(function*() {
let style = "div { position: absolute; top: 42px; left: 42px; height: 100px; width: 100px; border: 10px solid black; padding: 20px; margin: 30px auto;}";
let style = "div { position: absolute; top: 42px; left: 42px; height: 100.111px; width: 100px; border: 10px solid black; padding: 20px; margin: 30px auto;}";
let html = "<style>" + style + "</style><div></div>"
yield addTab("data:text/html," + encodeURIComponent(html));

View File

@ -422,7 +422,7 @@ LayoutView.prototype = {
// might be missing.
continue;
}
let parsedValue = parseInt(layout[property]);
let parsedValue = parseFloat(layout[property]);
if (Number.isNaN(parsedValue)) {
// Not a number. We use the raw string.
// Useful for "position" for example.
@ -451,9 +451,10 @@ LayoutView.prototype = {
width -= this.map.borderLeft.value + this.map.borderRight.value +
this.map.paddingLeft.value + this.map.paddingRight.value;
width = parseFloat(width.toPrecision(6));
height -= this.map.borderTop.value + this.map.borderBottom.value +
this.map.paddingTop.value + this.map.paddingBottom.value;
height = parseFloat(height.toPrecision(6));
let newValue = width + "\u00D7" + height;
if (this.sizeLabel.textContent != newValue) {

View File

@ -129,6 +129,8 @@ function updateUI() {
warningsNode.appendChild(li);
}
}
AppManager.update("details");
}
function showPrepackageLog() {

View File

@ -153,11 +153,11 @@ function doOK() {
target.remove(false);
AppProjects.addPackaged(folder).then((project) => {
window.arguments[0].location = project.location;
AppManager.validateProject(project).then(() => {
AppManager.validateAndUpdateProject(project).then(() => {
if (project.manifest) {
project.manifest.name = projectName;
AppManager.writeManifest(project).then(() => {
AppManager.validateProject(project).then(
AppManager.validateAndUpdateProject(project).then(
() => {window.close()}, bail)
}, bail)
} else {

View File

@ -134,7 +134,7 @@ let UI = {
AppManager.selectedProject.type != "mainProcess" &&
AppManager.selectedProject.type != "runtimeApp" &&
AppManager.selectedProject.type != "tab") {
AppManager.validateProject(AppManager.selectedProject);
AppManager.validateAndUpdateProject(AppManager.selectedProject);
}
// Hook to display promotional Developer Edition doorhanger. Only displayed once.
@ -567,7 +567,7 @@ let UI = {
menuindex: 1
});
this.projecteditor.on("onEditorSave", (editor, resource) => {
AppManager.validateProject(AppManager.selectedProject);
AppManager.validateAndUpdateProject(AppManager.selectedProject);
});
return this.projecteditor.loaded;
},
@ -667,9 +667,6 @@ let UI = {
}
}
// Validate project
yield AppManager.validateProject(project);
// Select project
AppManager.selectedProject = project;
}),
@ -1045,9 +1042,6 @@ let Cmds = {
// Retrieve added project
let project = AppProjects.get(ret.location);
// Validate project
yield AppManager.validateProject(project);
// Select project
AppManager.selectedProject = project;
@ -1106,7 +1100,7 @@ let Cmds = {
// The result of the validation process (storing names, icons, …) is not stored in
// the IndexedDB database when App Manager v1 is used.
// We need to run the validation again and update the name and icon of the app.
AppManager.validateProject(project).then(() => {
AppManager.validateAndUpdateProject(project).then(() => {
panelItemNode.setAttribute("label", project.name);
panelItemNode.setAttribute("image", project.icon);
});

View File

@ -278,36 +278,36 @@ let AppManager = exports.AppManager = {
},
_selectedProject: null,
set selectedProject(value) {
set selectedProject(project) {
// A regular comparison still sees a difference when equal in some cases
if (JSON.stringify(this._selectedProject) !==
JSON.stringify(value)) {
let cancelled = false;
this.update("before-project", { cancel: () => { cancelled = true; } });
if (cancelled) {
return;
}
this._selectedProject = value;
// Clear out tab store's selected state, if any
this.tabStore.selectedTab = null;
if (this.selectedProject) {
if (this.selectedProject.type == "packaged" ||
this.selectedProject.type == "hosted") {
this.validateProject(this.selectedProject);
}
if (this.selectedProject.type == "tab") {
this.tabStore.selectedTab = this.selectedProject.app;
}
}
this.update("project");
this.checkIfProjectIsRunning();
if (JSON.stringify(this._selectedProject) ===
JSON.stringify(project)) {
return;
}
let cancelled = false;
this.update("before-project", { cancel: () => { cancelled = true; } });
if (cancelled) {
return;
}
this._selectedProject = project;
// Clear out tab store's selected state, if any
this.tabStore.selectedTab = null;
if (project) {
if (project.type == "packaged" ||
project.type == "hosted") {
this.validateAndUpdateProject(project);
}
if (project.type == "tab") {
this.tabStore.selectedTab = project.app;
}
}
this.update("project");
this.checkIfProjectIsRunning();
},
get selectedProject() {
return this._selectedProject;
@ -323,6 +323,19 @@ let AppManager = exports.AppManager = {
return AppProjects.remove(location);
},
packageProject: Task.async(function*(project) {
if (!project) {
return;
}
if (project.type == "packaged" ||
project.type == "hosted") {
yield ProjectBuilding.build({
project: project,
logger: this.update.bind(this, "pre-package")
});
}
}),
_selectedRuntime: null,
set selectedRuntime(value) {
this._selectedRuntime = value;
@ -481,12 +494,9 @@ let AppManager = exports.AppManager = {
return Task.spawn(function* () {
let self = AppManager;
let packageDir = yield ProjectBuilding.build({
project: project,
logger: self.update.bind(self, "pre-package")
});
yield self.validateProject(project);
// Package and validate project
yield self.packageProject(project);
yield self.validateAndUpdateProject(project);
if (project.errorsCount > 0) {
self.reportError("error_cantInstallValidationErrors");
@ -501,7 +511,7 @@ let AppManager = exports.AppManager = {
let response;
if (project.type == "packaged") {
packageDir = packageDir || project.location;
let packageDir = yield ProjectBuilding.getPackageDir(project);
console.log("Installing app from " + packageDir);
response = yield self._appsFront.installPackaged(packageDir,
@ -557,14 +567,20 @@ let AppManager = exports.AppManager = {
/* PROJECT VALIDATION */
validateProject: function(project) {
validateAndUpdateProject: function(project) {
if (!project) {
return promise.reject();
}
return Task.spawn(function* () {
let validation = new AppValidator(project);
let packageDir = yield ProjectBuilding.getPackageDir(project);
let validation = new AppValidator({
type: project.type,
// Build process may place the manifest in a non-root directory
location: packageDir
});
yield validation.validate();
if (validation.manifest) {
@ -584,7 +600,7 @@ let AppManager = exports.AppManager = {
let origin = Services.io.newURI(manifestURL.prePath, null, null);
project.icon = Services.io.newURI(iconPath, null, origin).spec;
} else if (project.type == "packaged") {
let projectFolder = FileUtils.File(project.location);
let projectFolder = FileUtils.File(packageDir);
let folderURI = Services.io.newFileURI(projectFolder).spec;
project.icon = folderURI + iconPath.replace(/^\/|\\/, "");
}

View File

@ -14,10 +14,10 @@ const ProjectBuilding = exports.ProjectBuilding = {
let manifestPath = OS.Path.join(project.location, "package.json");
let exists = yield OS.File.exists(manifestPath);
if (!exists) {
return;
// No explicit manifest, try to generate one if possible
return this.generatePackageManifest(project);
}
let Decoder = new TextDecoder();
let data = yield OS.File.read(manifestPath);
data = new TextDecoder().decode(data);
let manifest;
@ -30,6 +30,31 @@ const ProjectBuilding = exports.ProjectBuilding = {
return manifest;
}),
/**
* For common frameworks in the community, attempt to detect the build
* settings if none are defined. This makes it much easier to get started
* with WebIDE. Later on, perhaps an add-on could define such things for
* different frameworks.
*/
generatePackageManifest: Task.async(function*(project) {
// Cordova
let cordovaConfigPath = OS.Path.join(project.location, "config.xml");
let exists = yield OS.File.exists(cordovaConfigPath);
if (!exists) {
return;
}
let data = yield OS.File.read(cordovaConfigPath);
data = new TextDecoder().decode(data);
if (data.contains("cordova.apache.org")) {
return {
"webide": {
"prepackage": "cordova prepare",
"packageDir": "./platforms/firefoxos/www"
}
};
}
}),
hasPrepackage: Task.async(function* (project) {
let manifest = yield ProjectBuilding.fetchPackageManifest(project);
return manifest && manifest.webide && "prepackage" in manifest.webide;
@ -37,22 +62,19 @@ const ProjectBuilding = exports.ProjectBuilding = {
// If the app depends on some build step, run it before pushing the app
build: Task.async(function* ({ project, logger }) {
if (!this.hasPrepackage(project)) {
if (!(yield this.hasPrepackage(project))) {
return;
}
let manifest = yield ProjectBuilding.fetchPackageManifest(project);
logger("start");
let packageDir;
try {
packageDir = yield this._build(project, manifest, logger);
yield this._build(project, manifest, logger);
logger("succeed");
} catch(e) {
logger("failed", e);
}
return packageDir;
}),
_build: Task.async(function* (project, manifest, logger) {
@ -61,6 +83,15 @@ const ProjectBuilding = exports.ProjectBuilding = {
let command, cwd, args = [], env = [];
// Copy frequently used env vars
let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
["HOME", "PATH"].forEach(key => {
let value = envService.get(key);
if (value) {
env.push(key + "=" + value);
}
});
if (typeof(manifest.prepackage) === "string") {
command = manifest.prepackage.replace(/%project%/g, project.location);
} else if (manifest.prepackage.command) {
@ -69,16 +100,9 @@ const ProjectBuilding = exports.ProjectBuilding = {
args = manifest.prepackage.args || [];
args = args.map(a => a.replace(/%project%/g, project.location));
env = manifest.prepackage.env || [];
env = env.concat(manifest.prepackage.env || []);
env = env.map(a => a.replace(/%project%/g, project.location));
// Gaia build system crashes if HOME env variable isn't set...
let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
let home = envService.get("HOME");
if (home) {
env.push("HOME=" + home);
}
if (manifest.prepackage.cwd) {
// Normalize path for Windows support (converts / to \)
let path = OS.Path.normalize(manifest.prepackage.cwd);
@ -110,7 +134,6 @@ const ProjectBuilding = exports.ProjectBuilding = {
// Otherwise, on Linux and Mac, SHELL env variable should refer to
// the user chosen shell program.
// (We do not check for OS, as on windows, with cygwin, ComSpec isn't set)
let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
let shell = envService.get("ComSpec") || envService.get("SHELL");
args.unshift(command);
@ -155,17 +178,22 @@ const ProjectBuilding = exports.ProjectBuilding = {
throw new Error("Unable to run pre-package command '" + command + "' " +
args.join(" ") + ":\n" + (e.message || e));
}
if (manifest.packageDir) {
let packageDir = OS.Path.join(project.location, manifest.packageDir);
// On Windows, replace / by \\
packageDir = OS.Path.normalize(packageDir);
let exists = yield OS.File.exists(packageDir);
if (exists) {
return packageDir;
}
throw new Error("Unable to resolve application package directory: '" + manifest.packageDir + "'");
}
}),
};
getPackageDir: Task.async(function*(project) {
let manifest = yield ProjectBuilding.fetchPackageManifest(project);
if (!manifest || !manifest.webide || !manifest.webide.packageDir) {
return project.location;
}
manifest = manifest.webide;
let packageDir = OS.Path.join(project.location, manifest.packageDir);
// On Windows, replace / by \\
packageDir = OS.Path.normalize(packageDir);
let exists = yield OS.File.exists(packageDir);
if (exists) {
return packageDir;
}
throw new Error("Unable to resolve application package directory: '" + manifest.packageDir + "'");
})
};

View File

@ -103,8 +103,10 @@ function nextTick() {
}
function waitForUpdate(win, update) {
info("Wait: " + update);
let deferred = promise.defer();
win.AppManager.on("app-manager-update", function onUpdate(e, what) {
info("Got: " + what);
if (what !== update) {
return;
}

View File

@ -26,7 +26,7 @@
let AppManager = win.AppManager;
function isProjectMarkedAsValid() {
let details = win.UI.projecteditor.window.frames[0];
let details = win.frames[0];
return !details.document.body.classList.contains("error");
}
@ -40,13 +40,10 @@
let packagedAppLocation = getTestFilePath("build_app" + testSuffix + "1");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "details");
let project = win.AppManager.selectedProject;
let deferred = promise.defer();
win.UI.projecteditor.once("onEditorCreated", deferred.resolve);
yield deferred.promise;
ok(!project.manifest, "manifest includes name");
is(project.name, "--", "Display name uses manifest name");
@ -55,18 +52,19 @@
loggedMessages.push(msg);
}
let packageDir = yield ProjectBuilding.build({
yield ProjectBuilding.build({
project,
logger
});
ok(!packageDir, "no custom packagedir");
let packageDir = yield ProjectBuilding.getPackageDir(project);
is(packageDir, packagedAppLocation, "no custom packagedir");
is(loggedMessages[0], "start", "log messages are correct");
ok(loggedMessages[1].indexOf("Running pre-package hook") != -1, "log messages are correct");
is(loggedMessages[2], "Terminated with error code: 0", "log messages are correct");
is(loggedMessages[3], "succeed", "log messages are correct");
// Trigger validation
yield AppManager.validateProject(AppManager.selectedProject);
yield AppManager.validateAndUpdateProject(AppManager.selectedProject);
yield nextTick();
ok("name" in project.manifest, "manifest includes name");
@ -79,14 +77,16 @@
packagedAppLocation = getTestFilePath("build_app" + testSuffix + "2");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
loggedMessages = [];
packageDir = yield ProjectBuilding.build({
yield ProjectBuilding.build({
project,
logger
});
packageDir = yield ProjectBuilding.getPackageDir(project);
is(OS.Path.normalize(packageDir),
OS.Path.join(packagedAppLocation, "stage"), "custom packagedir");
is(loggedMessages[0], "start", "log messages are correct");
@ -96,6 +96,7 @@
// Switch to the package dir in order to verify the generated webapp.manifest
yield win.Cmds.importPackagedApp(packageDir);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
@ -115,5 +116,3 @@
</script>
</body>
</html>

View File

@ -28,14 +28,17 @@
info("to call importPackagedApp(" + packagedAppLocation + ")");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
yield nextTick();
info("to call importHostedApp(" + hostedAppManifest + ")");
yield win.Cmds.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
yield nextTick();
info("to call importPackagedApp(" + packagedAppLocation + ") again");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
let project = win.AppManager.selectedProject;
is(project.location, packagedAppLocation, "Correctly reselected existing packaged app.");
@ -43,6 +46,7 @@
info("to call importHostedApp(" + hostedAppManifest + ") again");
yield win.Cmds.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
is(project.location, hostedAppManifest, "Correctly reselected existing hosted app.");
yield nextTick();
@ -71,4 +75,3 @@
</script>
</body>
</html>

View File

@ -29,6 +29,7 @@
ok(!win.UI._busyPromise, "UI is not busy");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
let project = win.AppManager.selectedProject;
is(project.location, packagedAppLocation, "Location is valid");
@ -40,6 +41,7 @@
let hostedAppManifest = TEST_BASE + "hosted_app.manifest";
yield win.Cmds.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
is(project.location, hostedAppManifest, "Location is valid");
@ -49,6 +51,7 @@
hostedAppManifest = TEST_BASE + "/app";
yield win.Cmds.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
ok(project.location.endsWith('manifest.webapp'), "The manifest was found and the project was updated");

View File

@ -25,20 +25,17 @@
let AppManager = win.AppManager;
function isProjectMarkedAsValid() {
let details = win.UI.projecteditor.window.frames[0];
let details = win.frames[0];
return !details.document.body.classList.contains("error");
}
let packagedAppLocation = getTestFilePath("app");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "details");
let project = win.AppManager.selectedProject;
let deferred = promise.defer();
win.UI.projecteditor.once("onEditorCreated", deferred.resolve);
yield deferred.promise;
ok("name" in project.manifest, "manifest includes name");
is(project.name, project.manifest.name, "Display name uses manifest name");
ok(isProjectMarkedAsValid(), "project is marked as valid");
@ -66,7 +63,7 @@
yield OS.File.writeAtomic(manifestPath, data , {tmpPath: manifestPath + ".tmp"});
// Trigger validation
yield AppManager.validateProject(AppManager.selectedProject);
yield AppManager.validateAndUpdateProject(AppManager.selectedProject);
yield nextTick();
ok(!("name" in project.manifest), "manifest has been updated");
@ -78,7 +75,7 @@
yield AppManager.writeManifest(project);
// Trigger validation
yield AppManager.validateProject(AppManager.selectedProject);
yield AppManager.validateAndUpdateProject(AppManager.selectedProject);
yield nextTick();
ok("name" in project.manifest, "manifest includes name");
@ -97,5 +94,3 @@
</script>
</body>
</html>

View File

@ -67,6 +67,7 @@
let packagedAppLocation = getTestFilePath("app");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
let panelNode = win.document.querySelector("#runtime-panel");
let items = panelNode.querySelectorAll(".runtime-panel-item-usb");

View File

@ -113,6 +113,7 @@
return Task.spawn(function*() {
let packagedAppLocation = getTestFilePath("app");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
});
}

View File

@ -46,6 +46,10 @@ function sendMessage(action, data, sync, callbackCookie) {
return Components.utils.cloneInto(result, content);
}
function enableDebug() {
sendAsyncMessage('Shumway:enableDebug', null);
}
addMessageListener('Shumway:init', function (message) {
sendAsyncMessage('Shumway:running', {}, {
externalInterface: externalInterfaceWrapper
@ -55,6 +59,7 @@ addMessageListener('Shumway:init', function (message) {
// up Xray wrappers.
shumwayComAdapter = Components.utils.createObjectIn(content, {defineAs: 'ShumwayCom'});
Components.utils.exportFunction(sendMessage, shumwayComAdapter, {defineAs: 'sendMessage'});
Components.utils.exportFunction(enableDebug, shumwayComAdapter, {defineAs: 'enableDebug'});
Object.defineProperties(shumwayComAdapter, {
onLoadFileCallback: { value: null, writable: true },
onExternalCallback: { value: null, writable: true },

View File

@ -0,0 +1,130 @@
/*
* Copyright 2015 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Simple class for synchronous XHR communication.
// See also examples/inspector/debug/server.js.
var PingPongConnection = (function () {
function PingPongConnection(url, onlySend) {
this.url = url;
this.onData = null;
this.onError = null;
this.currentXhr = null;
this.closed = false;
if (!onlySend) {
this.idle();
}
}
PingPongConnection.prototype = {
idle: function () {
function requestIncoming(connection) {
var xhr = new XMLHttpRequest();
xhr.open('GET', connection.url + '?idle', true);
xhr.onload = function () {
if (xhr.status === 204 &&
xhr.getResponseHeader('X-PingPong-Error') === 'timeout') {
requestIncoming(connection);
return;
}
if (xhr.status === 200) {
var result;
if (connection.onData) {
var response = xhr.responseText;
result = connection.onData(response ? JSON.parse(response) : undefined);
}
if (xhr.getResponseHeader('X-PingPong-Async') === '1') {
requestIncoming(connection);
} else {
sendResponse(connection, result);
}
return;
}
if (connection.onError) {
connection.onError(xhr.statusText);
}
};
xhr.onerror = function () {
if (connection.onError) {
connection.onError(xhr.error);
}
};
xhr.send();
connection.currentXhr = xhr;
}
function sendResponse(connection, result) {
var xhr = new XMLHttpRequest();
xhr.open('POST', connection.url + '?response', false);
xhr.onload = function () {
if (xhr.status !== 204) {
if (connection.onError) {
connection.onError(xhr.statusText);
}
}
requestIncoming(connection);
};
xhr.onerror = function () {
if (connection.onError) {
connection.onError(xhr.error);
}
};
xhr.send(result === undefined ? '' : JSON.stringify(result));
connection.currentXhr = xhr;
}
requestIncoming(this);
},
send: function (data, async, timeout) {
if (this.closed) {
throw new Error('connection closed');
}
async = !!async;
timeout |= 0;
var encoded = data === undefined ? '' : JSON.stringify(data);
if (async) {
var xhr = new XMLHttpRequest();
xhr.open('POST', this.url + '?async', true);
xhr.send(encoded);
return;
} else {
var xhr = new XMLHttpRequest();
xhr.open('POST', this.url, false);
if (timeout > 0) {
xhr.setRequestHeader('X-PingPong-Timeout', timeout);
}
xhr.send(encoded);
if (xhr.status === 204 &&
xhr.getResponseHeader('X-PingPong-Error') === 'timeout') {
throw new Error('sync request timeout');
}
var response = xhr.responseText;
return response ? JSON.parse(response) : undefined;
}
},
close: function () {
if (this.currentXhr) {
this.currentXhr.abort();
this.currentXhr = null;
}
this.closed = true;
}
};
return PingPongConnection;
})();

View File

@ -36,11 +36,23 @@ limitations under the License.
line-height: 0;
border: 0px none;
}
body.remoteStopped {
background-color: red;
}
body.remoteDebug {
background-color: green;
}
body.remoteReload {
background-color: yellow;
}
</style>
</head>
<body>
<iframe id="viewer" src="resource://shumway/web/viewer.html" width="100%" height="100%" mozbrowser remote="true"></iframe>
<script src="chrome://shumway/content/pingpong.js"></script>
<script src="chrome://shumway/content/viewerDebugger.js"></script>
<script src="chrome://shumway/content/viewerWrapper.js"></script>
</body>
</html>

View File

@ -0,0 +1,90 @@
/*
* Copyright 2015 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Components.utils.import('resource://gre/modules/Services.jsm');
var DebugUtils = (function () {
var baseUrl = null;
function getBaseUrl() {
if (baseUrl === null) {
try {
baseUrl = Services.prefs.getCharPref('shumway.debug.url');
} catch (e) {
baseUrl = 'http://localhost:8010';
}
}
return baseUrl;
}
var uniqueId = (Date.now() % 888888) * 2 + 1;
function getEnabledDebuggerId(swfUrl) {
return new Promise(function (resolve) {
var url = getBaseUrl() + '/debugController/' + uniqueId;
var connection = new PingPongConnection(url);
connection.onData = function (data) {
if (data.action === 'setDebugger' && data.swfUrl === swfUrl) {
resolve(data.debuggerId);
}
};
try {
connection.send({action: 'getDebugger', swfUrl: swfUrl, swfId: uniqueId}, true);
} catch (e) {
// ignoring failed send request
}
setTimeout(function () {
resolve(0);
connection.close();
}, 500);
});
}
function enableDebug(swfUrl) {
var url = getBaseUrl() + '/debugController/' + uniqueId;
var connection = new PingPongConnection(url, true);
try {
connection.send({action: 'enableDebugging', swfUrl: swfUrl}, true);
} catch (e) {
// ignoring failed send request
}
connection.close();
}
function createDebuggerConnection(swfUrl) {
return getEnabledDebuggerId(swfUrl).then(function (debuggerId) {
if (!debuggerId) {
return null;
}
var url = getBaseUrl() + '/debug/' + uniqueId + '/' + debuggerId;
console.log('Starting remote debugger with ' + url);
return new PingPongConnection(url);
});
}
return {
get isEnabled() {
try {
return Services.prefs.getBoolPref('shumway.debug.enabled');
} catch (e) {
return false;
}
},
enableDebug: enableDebug,
createDebuggerConnection: createDebuggerConnection
};
})();

View File

@ -53,6 +53,7 @@ function runViewer() {
// ShumwayStreamConverter.
var shumwayComAdapter = Components.utils.createObjectIn(childWindow, {defineAs: 'ShumwayCom'});
Components.utils.exportFunction(sendMessage, shumwayComAdapter, {defineAs: 'sendMessage'});
Components.utils.exportFunction(enableDebug, shumwayComAdapter, {defineAs: 'enableDebug'});
Object.defineProperties(shumwayComAdapter, {
onLoadFileCallback: { value: null, writable: true },
onExternalCallback: { value: null, writable: true },
@ -117,6 +118,10 @@ function runViewer() {
return window.notifyShumwayMessage(detail);
});
messageManager.addMessageListener('Shumway:enableDebug', function (message) {
enableDebug();
});
window.onExternalCallback = function (call) {
return externalInterface.callback(JSON.stringify(call));
};
@ -135,7 +140,76 @@ function runViewer() {
messageManager.sendAsyncMessage('Shumway:init', {});
}
function handleDebug(connection) {
viewer.parentNode.removeChild(viewer); // we don't need viewer anymore
document.body.className = 'remoteDebug';
function sendMessage(data) {
var detail = {
action: data.action,
data: data.data,
sync: data.sync
};
if (data.callback) {
detail.callback = true;
detail.cookie = data.cookie;
}
return window.notifyShumwayMessage(detail);
}
connection.onData = function (data) {
switch (data.action) {
case 'sendMessage':
return sendMessage(data.detail);
case 'reload':
document.body.className = 'remoteReload';
setTimeout(function () {
window.top.location.reload();
}, 1000);
return;
}
};
window.onExternalCallback = function (call) {
return connection.send({action: 'onExternalCallback', detail: call});
};
window.onMessageCallback = function (response) {
return connection.send({action: 'onMessageCallback', detail: response});
};
window.onLoadFileCallback = function (args) {
if (args.array) {
args.array = Array.prototype.slice.call(args.array, 0);
}
return connection.send({action: 'onLoadFileCallback', detail: args}, true);
};
connection.send({action: 'runViewer'}, true);
}
function enableDebug() {
DebugUtils.enableDebug(window.swfUrlLoading);
setTimeout(function () {
window.top.location.reload();
}, 1000);
}
promise.then(function (oop) {
if (DebugUtils.isEnabled) {
DebugUtils.createDebuggerConnection(window.swfUrlLoading).then(function (debuggerConnection) {
if (debuggerConnection) {
handleDebug(debuggerConnection);
} else if (oop) {
handlerOOP();
} else {
handler();
}
});
return;
}
if (oop) {
handlerOOP();
} else {

View File

@ -411,7 +411,7 @@ ChromeActions.prototype = {
var position = e.loaded;
var data = new Uint8Array(xhr.response);
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId,
topic: "progress", array: data, loaded: e.loaded, total: e.total});
topic: "progress", array: data, loaded: position, total: e.total});
lastPosition = position;
if (limit && e.total >= limit) {
xhr.abort();
@ -755,6 +755,59 @@ function activateShumwayScripts(window, requestListener) {
return requestListener.receive.apply(requestListener, arguments);
};
window.wrappedJSObject.runViewer();
var parentWindow = window.parent;
var viewerWindow = window.viewer.contentWindow;
function activate(e) {
e.preventDefault();
viewerWindow.removeEventListener('mousedown', activate, true);
parentWindow.addEventListener('keydown', forwardKeyEvent, true);
parentWindow.addEventListener('keyup', forwardKeyEvent, true);
sendFocusEvent('focus');
parentWindow.addEventListener('blur', deactivate, true);
parentWindow.addEventListener('mousedown', deactivate, true);
viewerWindow.addEventListener('unload', deactivate, true);
}
function deactivate() {
parentWindow.removeEventListener('blur', deactivate, true);
parentWindow.removeEventListener('mousedown', deactivate, true);
viewerWindow.removeEventListener('unload', deactivate, true);
parentWindow.removeEventListener('keydown', forwardKeyEvent, true);
parentWindow.removeEventListener('keyup', forwardKeyEvent, true);
sendFocusEvent('blur');
viewerWindow.addEventListener('mousedown', activate, true);
}
function forwardKeyEvent(e) {
var event = viewerWindow.document.createEvent('KeyboardEvent');
event.initKeyEvent(e.type,
e.bubbles,
e.cancelable,
e.view,
e.ctrlKey,
e.altKey,
e.shiftKey,
e.metaKey,
e.keyCode,
e.charCode);
viewerWindow.dispatchEvent(event);
}
function sendFocusEvent(type) {
var event = viewerWindow.document.createEvent("UIEvent");
event.initEvent(type, false, true);
viewerWindow.dispatchEvent(event);
}
viewerWindow.addEventListener('mousedown', activate, true);
}
if (window.document.readyState === "interactive" ||
@ -1034,6 +1087,8 @@ ShumwayStreamConverterBase.prototype = {
return;
}
domWindow.swfUrlLoading = actions.url;
// Report telemetry on amount of swfs on the page
if (actions.isOverlay) {
// Looking for last actions with same baseUrl

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,2 @@
0.9.3775
a82ac47
0.10.182
0195a96

Binary file not shown.

View File

@ -111,6 +111,7 @@ limitations under the License.
<menuitem label="Open in Inspector" id="inspectorMenu"></menuitem>
<menuitem label="Report Problems" id="reportMenu"></menuitem>
<menuitem label="Reload in Adobe Flash Player" id="fallbackMenu" hidden></menuitem>
<menuitem label="Debug this SWF" id="debugMenu"></menuitem>
<menuitem label="About Shumway %version%..." id="aboutMenu"></menuitem>
</menu>
</section>

View File

@ -93,7 +93,7 @@ var viewerPlayerglobalInfo = {
catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json"
};
var builtinPath = SHUMWAY_ROOT + "avm2/generated/builtin/builtin.abc";
var builtinPath = SHUMWAY_ROOT + "libs/builtin.abc";
var playerWindow;
var playerWindowLoaded = new Promise(function(resolve) {
@ -165,6 +165,13 @@ function runViewer() {
var version = Shumway.version || '';
document.getElementById('aboutMenu').label =
document.getElementById('aboutMenu').label.replace('%version%', version);
var debugMenuEnabled = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.debug.enabled', def: false});
if (debugMenuEnabled) {
document.getElementById('debugMenu').addEventListener('click', enableDebug);
} else {
document.getElementById('debugMenu').remove();
}
}
function showURL() {
@ -205,6 +212,10 @@ function showAbout() {
window.open('http://areweflashyet.com/');
}
function enableDebug() {
ShumwayCom.enableDebug();
}
var movieUrl, movieParams, objectParams;
window.addEventListener("message", function handlerMessage(e) {

View File

@ -22,8 +22,7 @@ var viewerPlayerglobalInfo = {
catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json"
};
var avm2Root = SHUMWAY_ROOT + "avm2/";
var builtinPath = avm2Root + "generated/builtin/builtin.abc";
var builtinPath = SHUMWAY_ROOT + "libs/builtin.abc";
window.print = function(msg) {
console.log(msg);

View File

@ -3,7 +3,7 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY window.title "Exceptions">
<!ENTITY window.width "36em">
<!ENTITY window.width "45em">
<!ENTITY treehead.sitename.label "Site">
<!ENTITY treehead.status.label "Status">
@ -21,6 +21,8 @@
<!ENTITY allow.accesskey "A">
<!ENTITY windowClose.key "w">
<!ENTITY button.close.label "Close">
<!ENTITY button.close.accesskey "C">
<!ENTITY button.cancel.label "Cancel">
<!ENTITY button.cancel.accesskey "C">
<!ENTITY button.ok.label "Save Changes">
<!ENTITY button.ok.accesskey "S">

View File

@ -51,6 +51,7 @@ SEARCH_PATHS = [
'testing/marionette/client',
'testing/marionette/client/marionette',
'testing/marionette/transport',
'testing/marionette/driver',
'testing/mozbase/mozcrash',
'testing/mozbase/mozdebug',
'testing/mozbase/mozdevice',

View File

@ -1,4 +1,5 @@
marionette_transport.pth:testing/marionette/transport
marionette_driver.pth:testing/marionette/driver
marionette.pth:testing/marionette/client
blessings.pth:python/blessings
configobj.pth:python/configobj

View File

@ -87,7 +87,7 @@ nsDSURIContentListener::OnStartURIOpen(nsIURI* aURI, bool* aAbortOpen)
}
NS_IMETHODIMP
nsDSURIContentListener::DoContent(const char* aContentType,
nsDSURIContentListener::DoContent(const nsACString& aContentType,
bool aIsContentPreferred,
nsIRequest* aRequest,
nsIStreamListener** aContentHandler,
@ -131,7 +131,7 @@ nsDSURIContentListener::DoContent(const char* aContentType,
}
bool reuseCV = baseChannel && baseChannel == mExistingJPEGRequest &&
nsDependentCString(aContentType).EqualsLiteral("image/jpeg");
aContentType.EqualsLiteral("image/jpeg");
if (mExistingJPEGStreamListener && reuseCV) {
nsRefPtr<nsIStreamListener> copy(mExistingJPEGStreamListener);

View File

@ -7929,7 +7929,7 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
mFiredUnloadEvent = false;
nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
nsContentUtils::FindInternalContentViewer("text/html");
nsContentUtils::FindInternalContentViewer(NS_LITERAL_CSTRING("text/html"));
if (docFactory) {
nsCOMPtr<nsIPrincipal> principal;
@ -8887,7 +8887,7 @@ nsDocShell::RestoreFromHistory()
}
nsresult
nsDocShell::CreateContentViewer(const char* aContentType,
nsDocShell::CreateContentViewer(const nsACString& aContentType,
nsIRequest* aRequest,
nsIStreamListener** aContentHandler)
{
@ -9083,7 +9083,7 @@ nsDocShell::CreateContentViewer(const char* aContentType,
}
nsresult
nsDocShell::NewContentViewerObj(const char* aContentType,
nsDocShell::NewContentViewerObj(const nsACString& aContentType,
nsIRequest* aRequest, nsILoadGroup* aLoadGroup,
nsIStreamListener** aContentHandler,
nsIContentViewer** aViewer)

View File

@ -290,10 +290,10 @@ protected:
nsresult CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
nsIURI* aBaseURI,
bool aTryToSaveOldPresentation = true);
nsresult CreateContentViewer(const char* aContentType,
nsresult CreateContentViewer(const nsACString& aContentType,
nsIRequest* aRequest,
nsIStreamListener** aContentHandler);
nsresult NewContentViewerObj(const char* aContentType,
nsresult NewContentViewerObj(const nsACString& aContentType,
nsIRequest* aRequest, nsILoadGroup* aLoadGroup,
nsIStreamListener** aContentHandler,
nsIContentViewer** aViewer);

View File

@ -22,12 +22,12 @@ interface nsIPrincipal;
* The component is a service, so use GetService, not CreateInstance to get it.
*/
[scriptable, uuid(70905274-8494-4e39-b011-d559adde3733)]
[scriptable, uuid(e795239e-9d3c-47c4-b063-9e600fb3b287)]
interface nsIDocumentLoaderFactory : nsISupports {
nsIContentViewer createInstance(in string aCommand,
in nsIChannel aChannel,
in nsILoadGroup aLoadGroup,
in string aContentType,
in ACString aContentType,
in nsIDocShell aContainer,
in nsISupports aExtraInfo,
out nsIStreamListener aDocListenerResult);

View File

@ -89,7 +89,7 @@ nsWebNavigationInfo::IsTypeSupportedInternal(const nsCString& aType,
nsContentUtils::ContentViewerType vtype = nsContentUtils::TYPE_UNSUPPORTED;
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
nsContentUtils::FindInternalContentViewer(aType.get(), &vtype);
nsContentUtils::FindInternalContentViewer(aType, &vtype);
switch (vtype) {
case nsContentUtils::TYPE_UNSUPPORTED:

View File

@ -0,0 +1,554 @@
<!doctype html>
<html>
<head>
<meta charset=utf-8>
<title>Tests for the effect of setting a CSS animation's AnimationPlayer.startTime</title>
<style>
.animated-div {
margin-left: 10px;
/* Make it easier to calculate expected values: */
animation-timing-function: linear ! important;
}
@keyframes anim {
from { margin-left: 100px; }
to { margin-left: 200px; }
}
</style>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script type="text/javascript;version=1.8">
'use strict';
// TODO: add equivalent tests without an animation-delay, but first we need to
// change the timing of animationstart dispatch. (Right now the animationstart
// event will fire before the ready Promise is resolved if there is no
// animation-delay.)
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
// TODO: Once the computedTiming property is implemented, add checks to the
// checker helpers to ensure that computedTiming's properties are updated as
// expected.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
const CSS_ANIM_EVENTS = ['animationstart', 'animationiteration', 'animationend'];
const ANIM_DELAY_MS = 1000000; // 1000s
const ANIM_DUR_MS = 1000000; // 1000s
// Expected computed 'margin-left' values at points during the active interval:
// When we assert_between_inclusive using these values we could in theory cause
// intermittent failure due to long refresh driver delays, but since the active
// duration is 1000s long, a delay would need to be around 100s to cause that.
// If that's happening than we have issues that we should solve anyway, so a
// failure to make us look into that seems like a good thing.
const UNANIMATED_POSITION = 10;
const INITIAL_POSITION = 100;
const TEN_PCT_POSITION = 110;
const FIFTY_PCT_POSITION = 150;
const NINETY_PCT_POSITION = 190;
const END_POSITION = 200;
function addDiv(id)
{
var div = document.createElement('div');
div.setAttribute('class', 'animated-div');
if (id) {
div.setAttribute('id', id);
}
document.body.appendChild(div);
return div;
}
/**
* CSS animation events fire asynchronously after we set 'startTime'. This
* helper class allows us to handle such events using Promises.
*
* To use this class:
*
* var eventWatcher = new EventWatcher(watchedNode, eventTypes);
* eventWatcher.waitForEvent(eventType).then(function() {
* // Promise fulfilled
* checkStuff();
* makeSomeChanges();
* return eventWatcher.waitForEvent(nextEventType);
* }).then(function() {
* // Promise fulfilled
* checkMoreStuff();
* eventWatcher.stopWatching(); // all done - stop listening for events
* });
*
* This class will assert_true(false) if an event occurs when there is no
* Promise created by a waitForEvent() call waiting to be fulfilled, or if the
* event is of a different type to the type passed to waitForEvent. This helps
* provide test coverage to ensure that only events that are expected occur, in
* the correct order and with the correct timing. It also helps vastly simplify
* the already complex code below by avoiding lots of gnarly error handling
* code.
*/
function EventWatcher(watchedNode, eventTypes)
{
if (typeof eventTypes == 'string') {
eventTypes = [eventTypes];
}
var waitingFor = null;
function eventHandler(evt) {
if (!waitingFor) {
assert_true(false, 'Not expecting event, but got: ' + evt.type +
' targeting element #' + evt.target.getAttribute('id'));
return;
}
if (evt.type != waitingFor.types[0]) {
assert_true(false, 'Expected ' + waitingFor.types[0] + ' event but got ' +
evt.type + ' event');
return;
}
if (waitingFor.types.length > 1) {
// Pop first event from array
waitingFor.types.shift();
return;
}
// We need to null out waitingFor before calling the resolve function since
// the Promise's resolve handlers may call waitForEvent() which will need
// to set waitingFor.
var resolveFunc = waitingFor.resolve;
waitingFor = null;
resolveFunc(evt);
}
for (let event of eventTypes) {
watchedNode.addEventListener(event, eventHandler);
}
this.waitForEvent = function(type) {
if (typeof type != 'string') {
return Promise.reject('Event type not a string');
}
return this.waitForEvents([type]);
};
/**
* This is useful when two events are expected to fire one immediately after
* the other. This happens when we skip over the entire active interval for
* instance. In this case an 'animationstart' and an 'animationend' are fired
* and due to the asynchronous nature of Promise callbacks this won't work:
*
* eventWatcher.waitForEvent('animationstart').then(function() {
* return waitForEvent('animationend');
* }).then(...);
*
* It doesn't work because the 'animationend' listener is added too late,
* because the resolve handler for the first Promise is called asynchronously
* some time after the 'animationstart' event is called, rather than at the
* time the event reaches the watched element.
*/
this.waitForEvents = function(types) {
if (waitingFor) {
return Promise.reject('Already waiting for an event');
}
return new Promise(function(resolve, reject) {
waitingFor = {
types: types,
resolve: resolve,
reject: reject
};
});
};
this.stopWatching = function() {
for (let event of eventTypes) {
watchedNode.removeEventListener(event, eventHandler);
}
};
return this;
}
// The terms used for the naming of the following helper functions refer to
// terms used in the Web Animations specification for specific phases of an
// animation. The terms can be found here:
//
// http://w3c.github.io/web-animations/#animation-node-phases-and-states
//
// Note the distinction between "player start time" and "animation start time".
// The former is the start of the start delay. The latter is the start of the
// active interval. (If there is no delay, they are the same.)
// Called when startTime is set to the time the start delay would ideally
// start (not accounting for any delay to next paint tick).
function checkStateOnSettingStartTimeToAnimationCreationTime(player)
{
// We don't test player.startTime since our caller just set it.
assert_equals(player.playState, 'running',
'AnimationPlayer.playState should be "running" at the start of ' +
'the start delay');
assert_equals(player.source.target.style.animationPlayState, 'running',
'AnimationPlayer.source.target.style.animationPlayState should be ' +
'"running" at the start of the start delay');
var div = player.source.target;
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
assert_equals(marginLeft, UNANIMATED_POSITION,
'the computed value of margin-left should be unaffected ' +
'at the beginning of the start delay');
}
// Called when the ready Promise's callbacks should happen
function checkStateOnReadyPromiseResolved(player)
{
assert_less_than_equal(player.startTime, document.timeline.currentTime,
'AnimationPlayer.startTime should be less than the timeline\'s ' +
'currentTime on the first paint tick after animation creation');
assert_equals(player.playState, 'running',
'AnimationPlayer.playState should be "running" on the first paint ' +
'tick after animation creation');
assert_equals(player.source.target.style.animationPlayState, 'running',
'AnimationPlayer.source.target.style.animationPlayState should be ' +
'"running" on the first paint tick after animation creation');
var div = player.source.target;
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
assert_equals(marginLeft, UNANIMATED_POSITION,
'the computed value of margin-left should be unaffected ' +
'by an animation with a delay on ready Promise resolve');
}
// Called when startTime is set to the time the active interval starts.
function checkStateAtActiveIntervalStartTime(player)
{
// We don't test player.startTime since our caller just set it.
assert_equals(player.playState, 'running',
'AnimationPlayer.playState should be "running" at the start of ' +
'the active interval');
assert_equals(player.source.target.style.animationPlayState, 'running',
'AnimationPlayer.source.target.style.animationPlayState should be ' +
'"running" at the start of the active interval');
var div = player.source.target;
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
'the computed value of margin-left should be close to the value at the ' +
'beginning of the animation');
}
function checkStateAtFiftyPctOfActiveInterval(player)
{
// We don't test player.startTime since our caller just set it.
var div = player.source.target;
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
assert_equals(marginLeft, FIFTY_PCT_POSITION,
'the computed value of margin-left should be half way through the ' +
'animation at the midpoint of the active interval');
}
function checkStateAtNinetyPctOfActiveInterval(player)
{
// We don't test player.startTime since our caller just set it.
var div = player.source.target;
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
assert_between_inclusive(marginLeft, NINETY_PCT_POSITION, END_POSITION,
'the computed value of margin-left should be close to the value at the ' +
'end of the animation');
}
// Called when startTime is set to the time the active interval ends.
function checkStateAtActiveIntervalEndTime(player)
{
// We don't test player.startTime since our caller just set it.
assert_equals(player.playState, 'finished',
'AnimationPlayer.playState should be "finished" at the end of ' +
'the active interval');
assert_equals(player.source.target.style.animationPlayState, "running",
'AnimationPlayer.source.target.style.animationPlayState should be ' +
'"finished" at the end of the active interval');
var div = player.source.target;
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
assert_equals(marginLeft, UNANIMATED_POSITION,
'the computed value of margin-left should be unaffected ' +
'by the animation at the end of the active duration');
}
test(function()
{
var div = addDiv();
div.style.animation = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
var player = div.getAnimationPlayers()[0];
// Animations shouldn't start until the next paint tick, so:
assert_equals(player.startTime, null,
'AnimationPlayer.startTime should be unresolved when an animation ' +
'is initially created');
assert_equals(player.playState, "pending",
'AnimationPlayer.playState should be "pending" when an animation ' +
'is initially created');
assert_equals(player.source.target.style.animationPlayState, 'running',
'AnimationPlayer.source.target.style.animationPlayState should be ' +
'"running" when an animation is initially created');
// XXX Ideally we would have a test to check the ready Promise is initially
// unresolved, but currently there is no Web API to do that. Waiting for the
// ready Promise with a timeout doesn't work because the resolved callback
// will be called (async) regardless of whether the Promise was resolved in
// the past or is resolved in the future.
var currentTime = document.timeline.currentTime;
player.startTime = document.timeline.currentTime;
assert_approx_equals(player.startTime, currentTime, 0.0001, // rounding error
'Check setting of startTime actually works');
checkStateOnSettingStartTimeToAnimationCreationTime(player);
div.parentNode.removeChild(div);
}, 'Examine newly created Animation');
async_test(function(t) {
var div = addDiv();
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
div.style.animation = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
var player = div.getAnimationPlayers()[0];
player.ready.then(t.step_func(function() {
checkStateOnReadyPromiseResolved(player);
player.startTime = document.timeline.currentTime - ANIM_DELAY_MS; // jump to start of active interval
return eventWatcher.waitForEvent('animationstart');
})).then(t.step_func(function() {
checkStateAtActiveIntervalStartTime(player);
player.startTime = document.timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5; // 50% through active interval
checkStateAtFiftyPctOfActiveInterval(player);
player.startTime = document.timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.9; // 90% through active interval
checkStateAtNinetyPctOfActiveInterval(player);
player.startTime = document.timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS; // end of active interval
return eventWatcher.waitForEvent('animationend');
})).then(t.step_func(function() {
checkStateAtActiveIntervalEndTime(player);
eventWatcher.stopWatching();
div.parentNode.removeChild(div);
})).catch(t.step_func(function(reason) {
assert_true(false, reason);
})).then(function() {
t.done();
});
}, 'Skipping forward through animation');
async_test(function(t) {
var div = addDiv();
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
div.style.animation = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
var player = div.getAnimationPlayers()[0];
player.startTime = document.timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS; // end of active interval
// Skipping over the active interval will dispatch an 'animationstart' then
// an 'animationend' event. We need to wait for these events before we start
// testing going backwards since EventWatcher will fail the test if it gets
// an event that we haven't told it about.
eventWatcher.waitForEvents(['animationstart', 'animationend']).then(t.step_func(function() {
// Now we can start the tests for skipping backwards, but first we check
// that after the events we're still in the same end time state:
checkStateAtActiveIntervalEndTime(player);
player.startTime = document.timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.9; // 90% through active interval
// Despite going backwards from after the end of the animation to just
// before the end of the animation, we now expect an animationstart event
// because we went from outside to inside the active interval.
return eventWatcher.waitForEvent('animationstart');
})).then(t.step_func(function() {
checkStateAtNinetyPctOfActiveInterval(player);
player.startTime = document.timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5; // 50% through active interval
checkStateAtFiftyPctOfActiveInterval(player);
player.startTime = document.timeline.currentTime - ANIM_DELAY_MS; // jump to start of active interval
checkStateAtActiveIntervalStartTime(player);
player.startTime = document.timeline.currentTime;
// Despite going backwards from just after the active interval starts to
// the animation start time, we now expect an animationend event
// because we went from inside to outside the active interval.
return eventWatcher.waitForEvent('animationend');
})).then(t.step_func(function() {
checkStateOnReadyPromiseResolved(player);
eventWatcher.stopWatching();
div.parentNode.removeChild(div);
})).catch(t.step_func(function(reason) {
assert_true(false, reason);
})).then(function() {
t.done();
});
// This must come after we've set up the Promise chain, since requesting
// computed style will force events to be dispatched.
// XXX For some reason this fails occasionally (either the player.playState
// check or the marginLeft check).
//checkStateAtActiveIntervalEndTime(player);
}, 'Skipping backwards through animation');
// Here we have multiple tests to check that redundant startTime changes do NOT
// dispatch events. It's impossible to distinguish between events not being
// dispatched and events just taking an incredibly long time to dispatch
// without some sort of risk of creating intermittent failure. We have a short
// timeout here since we don't want to delay the completion of this test file
// waiting for a long time to make "more sure" events weren't dispatched rather
// than being late.
//
// We test:
//
// * before -> active, then back
// * before -> after, then back
// * active -> before, then back
// * active -> after, then back
// * after -> before, then back
// * after -> active, then back
//
// We do all these tests in a single async_test since that allows us to share
// the timeout that we use to wait so that this test file isn't delayed by the
// timeout time multiplied by number of tests.
async_test(function(t) {
var divs = new Array(6);
var eventWatchers = new Array(6);
var players = new Array(6);
for (let i = 0; i < 6; i++) {
divs[i] = addDiv();
eventWatchers[i] = new EventWatcher(divs[i], CSS_ANIM_EVENTS);
divs[i].style.animation = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
players[i] = divs[i].getAnimationPlayers()[0];
}
var beforeTime = document.timeline.currentTime - ANIM_DELAY_MS / 2;
var activeTime = document.timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS / 2;
var afterTime = document.timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS - ANIM_DELAY_MS / 2;
// before -> active, then back:
players[0].startTime = activeTime;
players[0].startTime = beforeTime;
// before -> after, then back:
players[1].startTime = afterTime;
players[1].startTime = beforeTime;
// active -> before, then back:
eventWatchers[2].waitForEvent('animationstart').then(function() {
players[2].startTime = beforeTime;
players[2].startTime = activeTime;
});
players[2].startTime = activeTime; // get us into the initial state
// active -> after, then back:
eventWatchers[3].waitForEvent('animationstart').then(function() {
players[3].startTime = afterTime;
players[3].startTime = activeTime;
});
players[3].startTime = activeTime; // get us into the initial state
// after -> before, then back:
eventWatchers[4].waitForEvents(['animationstart', 'animationend']).then(function() {
players[4].startTime = beforeTime;
players[4].startTime = afterTime;
});
players[4].startTime = afterTime; // get us into the initial state
// after -> active, then back:
eventWatchers[5].waitForEvents(['animationstart', 'animationend']).then(function() {
players[5].startTime = activeTime;
players[5].startTime = afterTime;
});
players[5].startTime = afterTime; // get us into the initial state
// See the long comment documenting this async_test for an explanation of
// why we have this timeout and its relationship to intermittent failure.
setTimeout(function() {
for (let i = 0; i < 6; i++) {
eventWatchers[i].stopWatching();
divs[i].parentNode.removeChild(divs[i]);
}
t.done();
}, 1000);
}, 'Redundant changes');
async_test(function(t) {
var div = addDiv();
div.style.animation = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
var player = div.getAnimationPlayers()[0];
player.ready.then(t.step_func(function() {
player.startTime = null;
return player.ready;
})).then(t.step_func(function() {
div.parentNode.removeChild(div);
})).catch(t.step_func(function(reason) {
assert_true(false, reason);
})).then(function() {
t.done();
});
}, 'Setting startTime to null');
async_test(function(t) {
var div = addDiv();
div.style.animation = 'anim 100s';
var player = div.getAnimationPlayers()[0];
player.ready.then(t.step_func(function() {
var savedStartTime = player.startTime;
assert_not_equals(player.startTime, null,
'AnimationPlayer.startTime not null on ready Promise resolve');
player.pause();
assert_equals(player.startTime, null,
'AnimationPlayer.startTime is null after paused');
assert_equals(player.playState, 'paused',
'AnimationPlayer.playState is "paused" after pause() call');
div.parentNode.removeChild(div);
})).catch(t.step_func(function(reason) {
assert_true(false, reason);
})).then(function() {
t.done();
});
}, 'AnimationPlayer.startTime after paused');
</script>
</body>
</html>

View File

@ -9,6 +9,7 @@ skip-if = buildapp == 'mulet'
[css-animations/test_animation-pausing.html]
[css-animations/test_animation-player-playstate.html]
[css-animations/test_animation-player-ready.html]
[css-animations/test_animation-player-starttime.html]
[css-animations/test_animation-target.html]
[css-animations/test_element-get-animation-players.html]
skip-if = buildapp == 'mulet'

View File

@ -507,15 +507,15 @@ AudioChannelService::ProcessContentOrNormalChannelIsActive(uint64_t aChildID)
void
AudioChannelService::SetDefaultVolumeControlChannel(int32_t aChannel,
bool aHidden)
bool aVisible)
{
SetDefaultVolumeControlChannelInternal(aChannel, aHidden,
SetDefaultVolumeControlChannelInternal(aChannel, aVisible,
CONTENT_PROCESS_ID_MAIN);
}
void
AudioChannelService::SetDefaultVolumeControlChannelInternal(int32_t aChannel,
bool aHidden,
bool aVisible,
uint64_t aChildID)
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
@ -525,15 +525,26 @@ AudioChannelService::SetDefaultVolumeControlChannelInternal(int32_t aChannel,
// If this child is in the background and mDefChannelChildID is set to
// others then it means other child in the foreground already set it's
// own default channel already.
if ((!aHidden && mDefChannelChildID != aChildID) ||
(mDefChannelChildID != aChildID &&
mDefChannelChildID != CONTENT_PROCESS_ID_UNKNOWN)) {
if (!aVisible && mDefChannelChildID != aChildID) {
return;
}
// Workaround for the call screen app. The call screen app is running on the
// main process, that will results in wrong visible state. Because we use the
// docshell's active state as visible state, the main process is always
// active. Therefore, we will see the strange situation that the visible
// state of the call screen is always true. If the mDefChannelChildID is set
// to others then it means other child in the foreground already set it's
// own default channel already.
// Summary :
// Child process : foreground app always can set type.
// Parent process : check the mDefChannelChildID.
else if (aChildID == CONTENT_PROCESS_ID_MAIN &&
mDefChannelChildID != CONTENT_PROCESS_ID_UNKNOWN) {
return;
}
mDefChannelChildID = aChildID;
nsString channelName;
mDefChannelChildID = aVisible ? aChildID : CONTENT_PROCESS_ID_UNKNOWN;
nsAutoString channelName;
if (aChannel == -1) {
channelName.AssignASCII("unknown");
} else {

View File

@ -99,7 +99,7 @@ public:
* AudioChannel enum.
*/
virtual void SetDefaultVolumeControlChannel(int32_t aChannel,
bool aHidden);
bool aVisible);
bool AnyAudioChannelIsActive();
@ -153,7 +153,7 @@ protected:
/* Send the default-volume-channel-changed notification */
void SetDefaultVolumeControlChannelInternal(int32_t aChannel,
bool aHidden, uint64_t aChildID);
bool aVisible, uint64_t aChildID);
AudioChannelState CheckTelephonyPolicy(AudioChannel aChannel,
uint64_t aChildID);

View File

@ -41,6 +41,7 @@ ProcessGlobal::Get()
NS_IMETHODIMP_(bool)
ProcessGlobal::MarkForCC()
{
MarkScopesForCC();
return mMessageManager ? mMessageManager->MarkForCC() : false;
}

View File

@ -16,6 +16,7 @@
#include "nsISHistory.h"
#include "nsISHEntry.h"
#include "nsISHContainer.h"
#include "nsITabChild.h"
#include "nsIWindowWatcher.h"
#include "mozilla/Services.h"
#include "nsIXULWindow.h"
@ -297,6 +298,15 @@ MarkWindowList(nsISimpleEnumerator* aWindowList, bool aCleanupJS,
nsCOMPtr<nsIDocShell> rootDocShell = window->GetDocShell();
MarkDocShell(rootDocShell, aCleanupJS, aPrepareForCC);
nsCOMPtr<nsITabChild> tabChild = do_GetInterface(rootDocShell);
if (tabChild) {
nsCOMPtr<nsIContentFrameMessageManager> mm;
tabChild->GetMessageManager(getter_AddRefs(mm));
if (mm) {
mm->MarkForCC();
}
}
}
}
}

View File

@ -6406,7 +6406,7 @@ nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal)
}
already_AddRefed<nsIDocumentLoaderFactory>
nsContentUtils::FindInternalContentViewer(const char* aType,
nsContentUtils::FindInternalContentViewer(const nsACString& aType,
ContentViewerType* aLoaderType)
{
if (aLoaderType) {
@ -6421,7 +6421,9 @@ nsContentUtils::FindInternalContentViewer(const char* aType,
nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
nsXPIDLCString contractID;
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, getter_Copies(contractID));
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
PromiseFlatCString(aType).get(),
getter_Copies(contractID));
if (NS_SUCCEEDED(rv)) {
docFactory = do_GetService(contractID);
if (docFactory && aLoaderType) {
@ -6435,7 +6437,7 @@ nsContentUtils::FindInternalContentViewer(const char* aType,
return docFactory.forget();
}
if (DecoderTraits::IsSupportedInVideoDocument(nsDependentCString(aType))) {
if (DecoderTraits::IsSupportedInVideoDocument(aType)) {
docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
if (docFactory && aLoaderType) {
*aLoaderType = TYPE_CONTENT;

View File

@ -2020,7 +2020,7 @@ public:
};
static already_AddRefed<nsIDocumentLoaderFactory>
FindInternalContentViewer(const char* aType,
FindInternalContentViewer(const nsACString& aType,
ContentViewerType* aLoaderType = nullptr);
/**

View File

@ -38,6 +38,5 @@ DEPRECATED_OPERATION(ShowModalDialog)
DEPRECATED_OPERATION(Window_Content)
DEPRECATED_OPERATION(SyncXMLHttpRequest)
DEPRECATED_OPERATION(DataContainerEvent)
DEPRECATED_OPERATION(SendAsBinary)
DEPRECATED_OPERATION(Window_Controllers)
DEPRECATED_OPERATION(ImportXULIntoContent)

View File

@ -1243,7 +1243,7 @@ nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
nsCOMPtr<nsIContentViewer> viewer;
nsCOMPtr<nsIStreamListener> listener;
rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
type.get(), nullptr, nullptr,
type, nullptr, nullptr,
getter_AddRefs(listener),
getter_AddRefs(viewer));
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -1682,6 +1682,17 @@ nsMessageManagerScriptExecutor::InitChildGlobalInternal(
return true;
}
void
nsMessageManagerScriptExecutor::MarkScopesForCC()
{
for (uint32_t i = 0; i < mAnonymousGlobalScopes.Length(); ++i) {
JSObject* obj = mAnonymousGlobalScopes[i];
if (obj) {
JS::ExposeObjectToActiveJS(obj);
}
}
}
NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver)
nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;

View File

@ -401,6 +401,8 @@ public:
nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mGlobal;
return ref.forget();
}
void MarkScopesForCC();
protected:
friend class nsMessageManagerScriptCx;
nsMessageManagerScriptExecutor() { MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor); }

View File

@ -68,7 +68,7 @@ interface nsIXMLHttpRequestUpload : nsIXMLHttpRequestEventTarget {
* you're aware of all the security implications. And then think twice about
* it.
*/
[scriptable, uuid(704e91dc-a3f6-4e4d-9f5f-4bb85159aeb7)]
[scriptable, uuid(6f54214c-7175-498d-9d2d-0429e38c2869)]
interface nsIXMLHttpRequest : nsISupports
{
/**
@ -210,15 +210,6 @@ interface nsIXMLHttpRequest : nsISupports
*/
void send([optional] in nsIVariant body);
/**
* A variant of the send() method used to send binary data.
*
* @param body The request body as a DOM string. The string data will be
* converted to a single-byte string by truncation (i.e., the
* high-order byte of each character will be discarded).
*/
void sendAsBinary(in DOMString body);
/**
* Sets a HTTP request header for HTTP requests. You must call open
* before setting the request headers.

View File

@ -129,6 +129,7 @@ nsInProcessTabChildGlobal::~nsInProcessTabChildGlobal()
NS_IMETHODIMP_(bool)
nsInProcessTabChildGlobal::MarkForCC()
{
MarkScopesForCC();
return mMessageManager ? mMessageManager->MarkForCC() : false;
}

View File

@ -552,26 +552,7 @@ IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType)
return false;
}
const char* typeFromExt;
nsresult rv = pluginHost->IsPluginEnabledForExtension(ext.get(), typeFromExt);
if (NS_SUCCEEDED(rv)) {
mimeType = typeFromExt;
return true;
}
return false;
}
bool
PluginExistsForType(const char* aMIMEType)
{
nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
if (!pluginHost) {
NS_NOTREACHED("No pluginhost");
return false;
}
return pluginHost->PluginExistsForType(aMIMEType);
return pluginHost->HavePluginForExtension(ext, mimeType);
}
///
@ -798,7 +779,7 @@ nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
}
nsRefPtr<nsPluginInstanceOwner> newOwner;
rv = pluginHost->InstantiatePluginInstance(mContentType.get(),
rv = pluginHost->InstantiatePluginInstance(mContentType,
mURI.get(), this,
getter_AddRefs(newOwner));
@ -1616,8 +1597,10 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI)
nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
NS_ASSERTION(IsJavaMIME(javaMIME),
"plugin.mime.java should be recognized as java");
nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
if (StringBeginsWith(classIDAttr, NS_LITERAL_STRING("java:")) &&
PluginExistsForType(javaMIME)) {
pluginHost &&
pluginHost->HavePluginForType(javaMIME)) {
newMime = javaMIME;
isJava = true;
} else {
@ -2600,7 +2583,7 @@ nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
" (sync %i, notify %i)", this, aOldType, aOldState.GetInternalValue(),
mType, ObjectState().GetInternalValue(), aSync, aNotify));
nsCOMPtr<nsIContent> thisContent =
nsCOMPtr<nsIContent> thisContent =
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
NS_ASSERTION(thisContent, "must be a content");
@ -2668,7 +2651,10 @@ nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
return eType_Document;
}
if (caps & eSupportPlugins && PluginExistsForType(aMIMEType.get())) {
nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
if ((caps & eSupportPlugins) &&
pluginHost &&
pluginHost->HavePluginForType(aMIMEType, nsPluginHost::eExcludeNone)) {
// ShouldPlay will handle checking for disabled plugins
return eType_Plugin;
}
@ -3206,7 +3192,8 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentTyp
aReason = eFallbackClickToPlay;
uint32_t enabledState = nsIPluginTag::STATE_DISABLED;
pluginHost->GetStateForType(mContentType, &enabledState);
pluginHost->GetStateForType(mContentType, nsPluginHost::eExcludeNone,
&enabledState);
if (nsIPluginTag::STATE_DISABLED == enabledState) {
aReason = eFallbackDisabled;
return false;
@ -3215,7 +3202,9 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentTyp
// Before we check permissions, get the blocklist state of this plugin to set
// the fallback reason correctly.
uint32_t blocklistState = nsIBlocklistService::STATE_NOT_BLOCKED;
pluginHost->GetBlocklistStateForType(mContentType.get(), &blocklistState);
pluginHost->GetBlocklistStateForType(mContentType,
nsPluginHost::eExcludeNone,
&blocklistState);
if (blocklistState == nsIBlocklistService::STATE_BLOCKED) {
// no override possible
aReason = eFallbackBlocklisted;
@ -3264,7 +3253,9 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentTyp
// code here wouldn't matter at all. Bug 775301 is tracking this.
if (!nsContentUtils::IsSystemPrincipal(topDoc->NodePrincipal())) {
nsAutoCString permissionString;
rv = pluginHost->GetPermissionStringForType(mContentType, permissionString);
rv = pluginHost->GetPermissionStringForType(mContentType,
nsPluginHost::eExcludeNone,
permissionString);
NS_ENSURE_SUCCESS(rv, false);
uint32_t permission;
rv = permissionManager->TestPermissionFromPrincipal(topDoc->NodePrincipal(),

View File

@ -275,7 +275,8 @@ nsPerformanceTiming::ConnectStartHighRes()
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return mZeroTime;
}
return TimeStampToDOMHighResOrFetchStart(mConnectStart);
return mConnectStart.IsNull() ? DomainLookupEndHighRes()
: TimeStampToDOMHighRes(mConnectStart);
}
DOMTimeMilliSec
@ -290,7 +291,8 @@ nsPerformanceTiming::ConnectEndHighRes()
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return mZeroTime;
}
return TimeStampToDOMHighResOrFetchStart(mConnectEnd);
return mConnectEnd.IsNull() ? DomainLookupEndHighRes()
: TimeStampToDOMHighRes(mConnectEnd);
}
DOMTimeMilliSec

View File

@ -351,14 +351,14 @@ protected:
int32_t mStartOffset;
int32_t mEndOffset;
bool mIsPositioned;
bool mIsDetached;
bool mMaySpanAnonymousSubtrees;
bool mInSelection;
bool mIsGenerated;
bool mStartOffsetWasIncremented;
bool mEndOffsetWasIncremented;
bool mEnableGravitationOnElementRemoval;
bool mIsPositioned : 1;
bool mIsDetached : 1;
bool mMaySpanAnonymousSubtrees : 1;
bool mInSelection : 1;
bool mIsGenerated : 1;
bool mStartOffsetWasIncremented : 1;
bool mEndOffsetWasIncremented : 1;
bool mEnableGravitationOnElementRemoval : 1;
#ifdef DEBUG
int32_t mAssertNextInsertOrAppendIndex;
nsINode* mAssertNextInsertOrAppendNode;

View File

@ -2350,60 +2350,6 @@ nsXMLHttpRequest::ChangeStateToDone()
}
}
NS_IMETHODIMP
nsXMLHttpRequest::SendAsBinary(const nsAString &aBody)
{
ErrorResult rv;
SendAsBinary(aBody, rv);
return rv.ErrorCode();
}
void
nsXMLHttpRequest::SendAsBinary(const nsAString &aBody,
ErrorResult& aRv)
{
char *data = static_cast<char*>(NS_Alloc(aBody.Length() + 1));
if (!data) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
if (GetOwner() && GetOwner()->GetExtantDoc()) {
GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSendAsBinary);
}
nsAString::const_iterator iter, end;
aBody.BeginReading(iter);
aBody.EndReading(end);
char *p = data;
while (iter != end) {
if (*iter & 0xFF00) {
NS_Free(data);
aRv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
return;
}
*p++ = static_cast<char>(*iter++);
}
*p = '\0';
nsCOMPtr<nsIInputStream> stream;
aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, aBody.Length(),
NS_ASSIGNMENT_ADOPT);
if (aRv.Failed()) {
NS_Free(data);
return;
}
nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
aRv = variant->SetAsISupports(stream);
if (aRv.Failed()) {
return;
}
aRv = Send(variant);
}
static nsresult
GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult,
uint64_t* aContentLength, nsACString& aContentType,
@ -2953,13 +2899,18 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
if (method.EqualsLiteral("POST")) {
AddLoadFlags(mChannel,
nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::INHIBIT_CACHING);
}
// When we are sync loading, we need to bypass the local cache when it would
// otherwise block us waiting for exclusive access to the cache. If we don't
// do this, then we could dead lock in some cases (see bug 309424).
else if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
} else {
// When we are sync loading, we need to bypass the local cache when it would
// otherwise block us waiting for exclusive access to the cache. If we don't
// do this, then we could dead lock in some cases (see bug 309424).
//
// Also don't block on the cache entry on async if it is busy - favoring parallelism
// over cache hit rate for xhr. This does not disable the cache everywhere -
// only in cases where more than one channel for the same URI is accessed
// simultanously.
AddLoadFlags(mChannel,
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
}
// Since we expect XML data, set the type hint accordingly

View File

@ -488,7 +488,6 @@ public:
}
aRv = Send(RequestBody(aStream));
}
void SendAsBinary(const nsAString& aBody, ErrorResult& aRv);
void Abort();

View File

@ -97,7 +97,13 @@ function updateProgress(e, data, testName) {
function sendData(s) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "progressserver.sjs?send");
xhr.sendAsBinary(s);
// The Blob constructor encodes String elements as UTF-8;
// for straight bytes, manually convert to ArrayBuffer first
var buffer = new Uint8Array(s.length);
for (var i = 0; i < s.length; ++i) {
buffer[i] = s.charCodeAt(i) & 0xff;
};
xhr.send(new Blob([buffer]));
}
function closeConn() {

View File

@ -6,6 +6,9 @@
var gExpectedStatus = null;
var gNextTestFunc = null;
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
var asyncXHR = {
load: function() {
var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
@ -39,6 +42,7 @@ function run_test_pt1() {
catch (e) {
}
ioService.offline = true;
prefs.setBoolPref("network.dns.offline-localhost", false);
gExpectedStatus = Components.results.NS_ERROR_OFFLINE;
gNextTestFunc = run_test_pt2;
@ -51,6 +55,7 @@ function run_test_pt2() {
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
ioService.offline = false;
prefs.clearUserPref("network.dns.offline-localhost");
gExpectedStatus = Components.results.NS_ERROR_CONNECTION_REFUSED;
gNextTestFunc = end_test;

View File

@ -3202,6 +3202,9 @@ NS_IMPL_RELEASE_INHERITED(TabChildGlobal, DOMEventTargetHelper)
NS_IMETHODIMP_(bool)
TabChildGlobal::MarkForCC()
{
if (mTabChild) {
mTabChild->MarkScopesForCC();
}
return mMessageManager ? mMessageManager->MarkForCC() : false;
}

View File

@ -153,8 +153,6 @@ SyncXMLHttpRequestWarning=Synchronous XMLHttpRequest on the main thread is depre
ImplicitMetaViewportTagFallback=No meta-viewport tag found. Please explicitly specify one to prevent unexpected behavioural changes in future versions. For more help https://developer.mozilla.org/en/docs/Mozilla/Mobile/Viewport_meta_tag
# LOCALIZATION NOTE: Do not translate "DataContainerEvent" or "CustomEvent"
DataContainerEventWarning=Use of DataContainerEvent is deprecated. Use CustomEvent instead.
# LOCALIZATION NOTE: Do not translate "sendAsBinary" or "send(Blob data)"
SendAsBinaryWarning=The non-standard sendAsBinary method is deprecated and will soon be removed. Use the standard send(Blob data) method instead.
# LOCALIZATION NOTE: Do not translate "window.controllers"
Window_ControllersWarning=window.controllers is deprecated. Do not use it for UA detection.
ImportXULIntoContentWarning=Importing XUL nodes into a content document is deprecated. This functionality may be removed soon.

View File

@ -1328,14 +1328,15 @@ public:
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aOnSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure,
uint64_t aWindowId, nsACString& aAudioLoopbackDev,
nsACString& aVideoLoopbackDev)
nsACString& aVideoLoopbackDev, bool aUseFakeDevices)
: mConstraints(aConstraints)
, mOnSuccess(aOnSuccess)
, mOnFailure(aOnFailure)
, mManager(MediaManager::GetInstance())
, mWindowId(aWindowId)
, mLoopbackAudioDevice(aAudioLoopbackDev)
, mLoopbackVideoDevice(aVideoLoopbackDev) {}
, mLoopbackVideoDevice(aVideoLoopbackDev)
, mUseFakeDevices(aUseFakeDevices) {}
void // NS_IMETHOD
Run()
@ -1343,7 +1344,7 @@ public:
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
nsRefPtr<MediaEngine> backend;
if (mConstraints.mFake)
if (mConstraints.mFake || mUseFakeDevices)
backend = new MediaEngineDefault(mConstraints.mFakeTracks);
else
backend = mManager->GetBackend(mWindowId);
@ -1387,6 +1388,7 @@ private:
// automated media tests only.
nsCString mLoopbackAudioDevice;
nsCString mLoopbackVideoDevice;
bool mUseFakeDevices;
};
MediaManager::MediaManager()
@ -1596,10 +1598,15 @@ MediaManager::GetUserMedia(
if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
c.mVideo.SetAsBoolean() = false;
}
bool fake = true;
if (!c.mFake &&
!Preferences::GetBool("media.navigator.streams.fake", false)) {
fake = false;
}
// Pass callbacks and MediaStreamListener along to GetUserMediaTask.
nsAutoPtr<GetUserMediaTask> task;
if (c.mFake) {
if (fake) {
// Fake stream from default backend.
task = new GetUserMediaTask(c, onSuccess.forget(),
onFailure.forget(), windowID, listener, mPrefs, new MediaEngineDefault(c.mFakeTracks));
@ -1711,7 +1718,7 @@ MediaManager::GetUserMedia(
// XXX No full support for picture in Desktop yet (needs proper UI)
if (privileged ||
(c.mFake && !Preferences::GetBool("media.navigator.permission.fake"))) {
(fake && !Preferences::GetBool("media.navigator.permission.fake"))) {
MediaManager::GetMessageLoop()->PostTask(FROM_HERE, task.forget());
} else {
bool isHTTPS = false;
@ -1805,12 +1812,14 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
Preferences::GetCString("media.audio_loopback_dev");
nsAdoptingCString loopbackVideoDevice =
Preferences::GetCString("media.video_loopback_dev");
bool useFakeStreams =
Preferences::GetBool("media.navigator.streams.fake", false);
MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
new GetUserMediaDevicesTask(
aConstraints, onSuccess.forget(), onFailure.forget(),
(aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
loopbackAudioDevice, loopbackVideoDevice));
loopbackAudioDevice, loopbackVideoDevice, useFakeStreams));
return NS_OK;
}

View File

@ -123,6 +123,16 @@ public:
SBR_DEBUG("item=%p length=%d offset=%llu",
item, item->mData->Length(), mOffset);
if (item->mData->Length() + mOffset >= aOffset) {
if (aOffset <= mOffset) {
break;
}
uint32_t offset = aOffset - mOffset;
mOffset += offset;
evicted += offset;
nsRefPtr<LargeDataBuffer> data = new LargeDataBuffer;
data->AppendElements(item->mData->Elements() + offset,
item->mData->Length() - offset);
item->mData = data;
break;
}
mOffset += item->mData->Length();

View File

@ -427,9 +427,15 @@ TrackBuffer::EvictData(double aPlaybackTime,
bool evicted = toEvict < (totalSize - aThreshold);
if (evicted) {
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
mCurrentDecoder->GetBuffered(ranges);
*aBufferStartTime = std::max(0.0, ranges->GetStartTime());
if (playingDecoder) {
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
playingDecoder->GetBuffered(ranges);
*aBufferStartTime = std::max(0.0, ranges->GetStartTime());
} else {
// We do not currently have data to play yet.
// Avoid evicting anymore data to minimize rebuffering time.
*aBufferStartTime = 0.0;
}
}
return evicted;

View File

@ -105,6 +105,8 @@ skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabl
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_errorCallbacks.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_forwarding_basicAudioVideoCombined.html]
skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
[test_peerConnection_noTrickleAnswer.html]
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
[test_peerConnection_noTrickleOffer.html]

View File

@ -731,6 +731,9 @@ DataChannelWrapper.prototype = {
*/
function PeerConnectionWrapper(label, configuration, h264) {
this.configuration = configuration;
if (configuration && configuration.label_suffix) {
label = label + "_" + configuration.label_suffix;
}
this.label = label;
this.whenCreated = Date.now();

View File

@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript;version=1.8">
createHTML({
bug: "931903",
title: "Forwarding a stream from a combined audio/video peerconnection to another"
});
runNetworkTest(function() {
var gumTest = new PeerConnectionTest();
var forwardingOptions = { config_local: { label_suffix: "forwarded" },
config_remote: { label_suffix: "forwarded" } };
var forwardingTest = new PeerConnectionTest(forwardingOptions);
gumTest.setMediaConstraints([{audio: true, video: true}], []);
forwardingTest.setMediaConstraints([{audio: true, video: true}], []);
forwardingTest.chain.replace("PC_LOCAL_GUM", [
function PC_FORWARDING_CAPTUREVIDEO(test) {
var streams = gumTest.pcRemote._pc.getRemoteStreams();
is(streams.length, 1, "One stream to forward");
is(streams[0].getTracks().length, 2, "Forwarded stream has 2 tracks");
forwardingTest.pcLocal.attachMedia(streams[0], 'audiovideo', 'local');
return Promise.resolve();
}
]);
gumTest.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT");
gumTest.chain.execute()
.then(() => forwardingTest.chain.execute())
.then(() => gumTest.close())
.then(() => forwardingTest.close())
.then(() => networkTestFinished());
});
</script>
</pre>
</body>
</html>

View File

@ -66,9 +66,21 @@ public:
MutexAutoLock lock(NodeMutex());
if (Node() &&
aInput.mChannelData.Length() > 0) {
nsRefPtr<TransferBuffer> transfer = new TransferBuffer(aStream, aInput);
if (Node()) {
// If the input is silent, we sill need to send a silent buffer
if (aOutput->IsNull()) {
AllocateAudioBlock(1, aOutput);
float* samples = static_cast<float*>(
const_cast<void*>(aOutput->mChannelData[0]));
PodZero(samples, WEBAUDIO_BLOCK_SIZE);
}
uint32_t channelCount = aOutput->mChannelData.Length();
for (uint32_t channel = 0; channel < channelCount; ++channel) {
float* samples = static_cast<float*>(
const_cast<void*>(aOutput->mChannelData[channel]));
AudioBlockInPlaceScale(samples, aOutput->mVolume);
}
nsRefPtr<TransferBuffer> transfer = new TransferBuffer(aStream, *aOutput);
NS_DispatchToMainThread(transfer);
}
}

View File

@ -26,6 +26,7 @@ support-files =
webaudio.js
[test_analyserNode.html]
[test_analyserScale.html]
[test_analyserNodeOutput.html]
[test_analyserNodePassThrough.html]
[test_AudioBuffer.html]

View File

@ -0,0 +1,59 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test AnalyserNode when the input is scaled </title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="webaudio.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
var context = new AudioContext();
var gain = context.createGain();
var analyser = context.createAnalyser();
var osc = context.createOscillator();
osc.connect(gain);
gain.connect(analyser);
osc.start();
var array = new Uint8Array(analyser.frequencyBinCount);
function getAnalyserData() {
gain.gain.setValueAtTime(currentGain, context.currentTime);
analyser.getByteTimeDomainData(array);
var inrange = true;
var max = -1;
for (var i = 0; i < array.length; i++) {
if (array[i] > max) {
max = Math.abs(array[i] - 128);
}
}
if (max <= currentGain * 128) {
ok(true, "Analyser got scaled data for " + currentGain);
currentGain = tests.shift();
if (currentGain == undefined) {
SimpleTest.finish();
return;
}
}
requestAnimationFrame(getAnalyserData);
}
var tests = [1.0, 0.5, 0.0];
var currentGain = tests.shift();
requestAnimationFrame(getAnalyserData);
});
</script>
</pre>
</body>
</html>

View File

@ -27,7 +27,7 @@ interface nsIPluginPlayPreviewInfo : nsISupports
boolean checkWhitelist(in AUTF8String pageURI, in AUTF8String objectURI);
};
[scriptable, uuid(6034e3a0-365b-48a5-a257-586946ff361e)]
[scriptable, uuid(d7d5b2e0-105b-4c9d-8558-b6b31f28b7df)]
interface nsIPluginHost : nsISupports
{
/**
@ -50,6 +50,12 @@ interface nsIPluginHost : nsISupports
const uint32_t FLAG_CLEAR_ALL = 0;
const uint32_t FLAG_CLEAR_CACHE = 1;
/*
* For use with Get*ForType functions
*/
const uint32_t EXCLUDE_NONE = 0;
const uint32_t EXCLUDE_DISABLED = 1 << 0;
/*
* Clear site data for a given plugin.
*
@ -106,7 +112,14 @@ interface nsIPluginHost : nsISupports
nsIPluginPlayPreviewInfo getPlayPreviewInfo(in AUTF8String mimeType);
ACString getPermissionStringForType(in AUTF8String mimeType);
/**
* Get the "permission string" for the plugin. This is a string that can be
* passed to the permission manager to see whether the plugin is allowed to
* run, for example. This will typically be based on the plugin's "nice name"
* and its blocklist state.
*/
ACString getPermissionStringForType(in AUTF8String mimeType,
[optional] in uint32_t excludeFlags);
/**
* Get the nsIPluginTag for this MIME type. This method works with both
@ -116,16 +129,20 @@ interface nsIPluginHost : nsISupports
* @throws NS_ERROR_NOT_AVAILABLE if no plugin is available for this MIME
* type.
*/
nsIPluginTag getPluginTagForType(in AUTF8String mimeType);
nsIPluginTag getPluginTagForType(in AUTF8String mimeType,
[optional] in uint32_t excludeFlags);
/**
* Get the nsIPluginTag state for this MIME type.
* Get the nsIPluginTag enabled state for this MIME type. See
* nsIPluginTag.enabledState.
*/
unsigned long getStateForType(in AUTF8String mimeType);
unsigned long getStateForType(in AUTF8String mimeType,
[optional] in uint32_t excludeFlags);
/**
* Get the blocklist state for a MIME type.
* Get the blocklist state for a MIME type. See nsIPluginTag.blocklistState.
*/
uint32_t getBlocklistStateForType(in string aMimeType);
uint32_t getBlocklistStateForType(in AUTF8String aMimeType,
[optional] in uint32_t excludeFlags);
};

View File

@ -5,7 +5,7 @@
#include "nsISupports.idl"
[scriptable, uuid(231df043-3a32-43c4-aaac-7ad2da81e84f)]
[scriptable, uuid(32563b21-3c5e-4864-baaa-4e49b90b64f2)]
interface nsIPluginTag : nsISupports
{
// enabledState is stored as one of the following as an integer in prefs,
@ -20,6 +20,9 @@ interface nsIPluginTag : nsISupports
readonly attribute AUTF8String version;
readonly attribute AUTF8String name;
// The 'nice' name of this plugin, e.g. 'flash' 'java'
readonly attribute AUTF8String niceName;
/**
* true only if this plugin is "hardblocked" and cannot be enabled.
*/
@ -30,8 +33,15 @@ interface nsIPluginTag : nsISupports
*/
readonly attribute boolean isEnabledStateLocked;
// If this plugin is capable of being used (not disabled, blocklisted, etc)
readonly attribute boolean active;
// Get a specific nsIBlocklistService::STATE_*
readonly attribute unsigned long blocklistState;
readonly attribute boolean disabled;
readonly attribute boolean clicktoplay;
// See the STATE_* values above.
attribute unsigned long enabledState;
readonly attribute PRTime lastModifiedTime;

View File

@ -277,7 +277,7 @@ nsNPAPIPluginInstance::StopTime()
return mStopTime;
}
nsresult nsNPAPIPluginInstance::Initialize(nsNPAPIPlugin *aPlugin, nsPluginInstanceOwner* aOwner, const char* aMIMEType)
nsresult nsNPAPIPluginInstance::Initialize(nsNPAPIPlugin *aPlugin, nsPluginInstanceOwner* aOwner, const nsACString& aMIMEType)
{
PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::Initialize this=%p\n",this));
@ -287,11 +287,8 @@ nsresult nsNPAPIPluginInstance::Initialize(nsNPAPIPlugin *aPlugin, nsPluginInsta
mPlugin = aPlugin;
mOwner = aOwner;
if (aMIMEType) {
mMIMEType = (char*)PR_Malloc(strlen(aMIMEType) + 1);
if (mMIMEType) {
PL_strcpy(mMIMEType, aMIMEType);
}
if (!aMIMEType.IsEmpty()) {
mMIMEType = ToNewCString(aMIMEType);
}
return Start();

View File

@ -81,7 +81,7 @@ private:
public:
NS_DECL_THREADSAFE_ISUPPORTS
nsresult Initialize(nsNPAPIPlugin *aPlugin, nsPluginInstanceOwner* aOwner, const char* aMIMEType);
nsresult Initialize(nsNPAPIPlugin *aPlugin, nsPluginInstanceOwner* aOwner, const nsACString& aMIMEType);
nsresult Start();
nsresult Stop();
nsresult SetWindow(NPWindow* window);

View File

@ -119,7 +119,7 @@ using mozilla::plugins::PluginAsyncSurrogate;
{ \
while (list_) { \
type_ temp = list_->mNext_; \
list_->mNext_ = nullptr; \
list_->mNext_ = nullptr; \
list_ = temp; \
} \
}
@ -500,17 +500,17 @@ nsresult nsPluginHost::GetURLWithHeaders(nsNPAPIPluginInstance* pluginInst,
}
nsresult nsPluginHost::PostURL(nsISupports* pluginInst,
const char* url,
uint32_t postDataLen,
const char* postData,
bool isFile,
const char* target,
nsNPAPIPluginStreamListener* streamListener,
const char* altHost,
const char* referrer,
bool forceJSEnabled,
uint32_t postHeadersLength,
const char* postHeaders)
const char* url,
uint32_t postDataLen,
const char* postData,
bool isFile,
const char* target,
nsNPAPIPluginStreamListener* streamListener,
const char* altHost,
const char* referrer,
bool forceJSEnabled,
uint32_t postHeadersLength,
const char* postHeaders)
{
nsresult rv;
@ -667,11 +667,6 @@ nsresult nsPluginHost::FindProxyForURL(const char* url, char* *result)
return res;
}
nsresult nsPluginHost::Init()
{
return NS_OK;
}
nsresult nsPluginHost::UnloadPlugins()
{
PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UnloadPlugins Called\n"));
@ -779,7 +774,7 @@ nsPluginHost::GetPluginTempDir(nsIFile **aDir)
}
nsresult
nsPluginHost::InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
nsPluginHost::InstantiatePluginInstance(const nsACString& aMimeType, nsIURI* aURL,
nsObjectLoadingContent *aContent,
nsPluginInstanceOwner** aOwner)
{
@ -792,12 +787,12 @@ nsPluginHost::InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
("nsPluginHost::InstantiatePlugin Begin mime=%s, url=%s\n",
aMimeType, urlSpec.get()));
PromiseFlatCString(aMimeType).get(), urlSpec.get()));
PR_LogFlush();
#endif
if (!aMimeType) {
if (aMimeType.IsEmpty()) {
NS_NOTREACHED("Attempting to spawn a plugin with no mime type");
return NS_ERROR_FAILURE;
}
@ -851,7 +846,7 @@ nsPluginHost::InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
("nsPluginHost::InstantiatePlugin Finished mime=%s, rv=%d, url=%s\n",
aMimeType, rv, urlSpec2.get()));
PromiseFlatCString(aMimeType).get(), rv, urlSpec2.get()));
PR_LogFlush();
#endif
@ -885,7 +880,7 @@ nsPluginHost::TagForPlugin(nsNPAPIPlugin* aPlugin)
return nullptr;
}
nsresult nsPluginHost::SetUpPluginInstance(const char *aMimeType,
nsresult nsPluginHost::SetUpPluginInstance(const nsACString &aMimeType,
nsIURI *aURL,
nsPluginInstanceOwner *aOwner)
{
@ -919,7 +914,7 @@ nsresult nsPluginHost::SetUpPluginInstance(const char *aMimeType,
}
nsresult
nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
nsPluginHost::TrySetUpPluginInstance(const nsACString &aMimeType,
nsIURI *aURL,
nsPluginInstanceOwner *aOwner)
{
@ -929,7 +924,7 @@ nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
("nsPluginHost::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n",
aMimeType, aOwner, urlSpec.get()));
PromiseFlatCString(aMimeType).get(), aOwner, urlSpec.get()));
PR_LogFlush();
#endif
@ -948,7 +943,7 @@ nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
return NS_ERROR_FAILURE;
}
nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
nsPluginTag* pluginTag = FindNativePluginForType(aMimeType, true);
NS_ASSERTION(pluginTag, "Must have plugin tag here!");
@ -992,7 +987,7 @@ nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
("nsPluginHost::TrySetupPluginInstance Finished mime=%s, rv=%d, owner=%p, url=%s\n",
aMimeType, rv, aOwner, urlSpec2.get()));
PromiseFlatCString(aMimeType).get(), rv, aOwner, urlSpec2.get()));
PR_LogFlush();
#endif
@ -1001,70 +996,77 @@ nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
}
bool
nsPluginHost::PluginExistsForType(const char* aMimeType)
nsPluginHost::HavePluginForType(const nsACString & aMimeType,
PluginFilter aFilter)
{
nsPluginTag *plugin = FindPluginForType(aMimeType, false);
return nullptr != plugin;
bool checkEnabled = aFilter & eExcludeDisabled;
return FindNativePluginForType(aMimeType, checkEnabled);
}
NS_IMETHODIMP
nsPluginHost::GetPluginTagForType(const nsACString& aMimeType,
uint32_t aExcludeFlags,
nsIPluginTag** aResult)
{
nsPluginTag* plugin = FindPluginForType(aMimeType.Data(), true);
if (!plugin) {
plugin = FindPluginForType(aMimeType.Data(), false);
bool includeDisabled = !(aExcludeFlags & eExcludeDisabled);
nsPluginTag *tag = FindNativePluginForType(aMimeType, true);
// Prefer enabled, but select disabled if none is found
if (includeDisabled && !tag) {
tag = FindNativePluginForType(aMimeType, false);
}
if (!plugin) {
if (!tag) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_ADDREF(*aResult = plugin);
NS_ADDREF(*aResult = tag);
return NS_OK;
}
NS_IMETHODIMP
nsPluginHost::GetStateForType(const nsACString &aMimeType, uint32_t* aResult)
nsPluginHost::GetStateForType(const nsACString &aMimeType,
uint32_t aExcludeFlags,
uint32_t* aResult)
{
nsPluginTag *plugin = FindPluginForType(aMimeType.Data(), true);
if (!plugin) {
plugin = FindPluginForType(aMimeType.Data(), false);
}
if (!plugin) {
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIPluginTag> tag;
nsresult rv = GetPluginTagForType(aMimeType, aExcludeFlags,
getter_AddRefs(tag));
NS_ENSURE_SUCCESS(rv, rv);
return plugin->GetEnabledState(aResult);
return tag->GetEnabledState(aResult);
}
NS_IMETHODIMP
nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState)
nsPluginHost::GetBlocklistStateForType(const nsACString &aMimeType,
uint32_t aExcludeFlags,
uint32_t *aState)
{
nsPluginTag *plugin = FindPluginForType(aMimeType, true);
if (!plugin) {
plugin = FindPluginForType(aMimeType, false);
}
if (!plugin) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPluginTag> tag;
nsresult rv = GetPluginTagForType(aMimeType,
aExcludeFlags,
getter_AddRefs(tag));
NS_ENSURE_SUCCESS(rv, rv);
*aState = plugin->GetBlocklistState();
return NS_OK;
return tag->GetBlocklistState(aState);
}
NS_IMETHODIMP
nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsACString &aPermissionString)
nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType,
uint32_t aExcludeFlags,
nsACString &aPermissionString)
{
nsCOMPtr<nsIPluginTag> tag;
nsresult rv = GetPluginTagForType(aMimeType, aExcludeFlags,
getter_AddRefs(tag));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(tag, NS_ERROR_FAILURE);
aPermissionString.Truncate();
uint32_t blocklistState;
nsresult rv = GetBlocklistStateForType(aMimeType.Data(), &blocklistState);
rv = tag->GetBlocklistState(&blocklistState);
NS_ENSURE_SUCCESS(rv, rv);
nsPluginTag *tag = FindPluginForType(aMimeType.Data(), true);
if (!tag) {
tag = FindPluginForType(aMimeType.Data(), false);
}
if (!tag) {
return NS_ERROR_FAILURE;
}
if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
@ -1074,45 +1076,23 @@ nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsACString
aPermissionString.AssignLiteral("plugin:");
}
aPermissionString.Append(tag->GetNiceFileName());
nsCString niceName;
rv = tag->GetNiceName(niceName);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(!niceName.IsEmpty(), NS_ERROR_FAILURE);
aPermissionString.Append(niceName);
return NS_OK;
}
// check comma delimitered extensions
static int CompareExtensions(const char *aExtensionList, const char *aExtension)
bool
nsPluginHost::HavePluginForExtension(const nsACString & aExtension,
/* out */ nsACString & aMimeType,
PluginFilter aFilter)
{
if (!aExtensionList || !aExtension)
return -1;
const char *pExt = aExtensionList;
const char *pComma = strchr(pExt, ',');
if (!pComma)
return PL_strcasecmp(pExt, aExtension);
int extlen = strlen(aExtension);
while (pComma) {
int length = pComma - pExt;
if (length == extlen && 0 == PL_strncasecmp(aExtension, pExt, length))
return 0;
pComma++;
pExt = pComma;
pComma = strchr(pExt, ',');
}
// the last one
return PL_strcasecmp(pExt, aExtension);
}
nsresult
nsPluginHost::IsPluginEnabledForExtension(const char* aExtension,
const char* &aMimeType)
{
nsPluginTag *plugin = FindPluginEnabledForExtension(aExtension, aMimeType);
if (plugin)
return NS_OK;
return NS_ERROR_FAILURE;
bool checkEnabled = aFilter & eExcludeDisabled;
return FindNativePluginForExtension(aExtension, aMimeType, checkEnabled);
}
void
@ -1185,10 +1165,10 @@ nsPluginHost::FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches)
}
nsPluginTag*
nsPluginHost::FindPluginForType(const char* aMimeType,
bool aCheckEnabled)
nsPluginHost::FindNativePluginForType(const nsACString & aMimeType,
bool aCheckEnabled)
{
if (!aMimeType) {
if (aMimeType.IsEmpty()) {
return nullptr;
}
@ -1198,14 +1178,9 @@ nsPluginHost::FindPluginForType(const char* aMimeType,
nsPluginTag *plugin = mPlugins;
while (plugin) {
if (!aCheckEnabled || plugin->IsActive()) {
int32_t mimeCount = plugin->mMimeTypes.Length();
for (int32_t i = 0; i < mimeCount; i++) {
if (0 == PL_strcasecmp(plugin->mMimeTypes[i].get(), aMimeType)) {
matchingPlugins.AppendElement(plugin);
break;
}
}
if ((!aCheckEnabled || plugin->IsActive()) &&
plugin->HasMimeType(aMimeType)) {
matchingPlugins.AppendElement(plugin);
}
plugin = plugin->mNext;
}
@ -1214,27 +1189,24 @@ nsPluginHost::FindPluginForType(const char* aMimeType,
}
nsPluginTag*
nsPluginHost::FindPluginEnabledForExtension(const char* aExtension,
const char*& aMimeType)
nsPluginHost::FindNativePluginForExtension(const nsACString & aExtension,
/* out */ nsACString & aMimeType,
bool aCheckEnabled)
{
if (!aExtension) {
if (aExtension.IsEmpty()) {
return nullptr;
}
LoadPlugins();
InfallibleTArray<nsPluginTag*> matchingPlugins;
nsCString matchingMime; // Don't mutate aMimeType unless returning a match
nsPluginTag *plugin = mPlugins;
while (plugin) {
if (plugin->IsActive()) {
int32_t variants = plugin->mExtensions.Length();
for (int32_t i = 0; i < variants; i++) {
// mExtensionsArray[cnt] is a list of extensions separated by commas
if (0 == CompareExtensions(plugin->mExtensions[i].get(), aExtension)) {
matchingPlugins.AppendElement(plugin);
break;
}
if (!aCheckEnabled || plugin->IsActive()) {
if (plugin->HasExtension(aExtension, matchingMime)) {
matchingPlugins.AppendElement(plugin);
}
}
plugin = plugin->mNext;
@ -1245,15 +1217,8 @@ nsPluginHost::FindPluginEnabledForExtension(const char* aExtension,
return nullptr;
}
int32_t variants = preferredPlugin->mExtensions.Length();
for (int32_t i = 0; i < variants; i++) {
// mExtensionsArray[cnt] is a list of extensions separated by commas
if (0 == CompareExtensions(preferredPlugin->mExtensions[i].get(), aExtension)) {
aMimeType = preferredPlugin->mMimeTypes[i].get();
break;
}
}
// Re-fetch the matching type because of how FindPreferredPlugin works...
preferredPlugin->HasExtension(aExtension, aMimeType);
return preferredPlugin;
}
@ -1364,27 +1329,26 @@ nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId)
NS_DispatchToMainThread(runnable);
}
nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
nsresult nsPluginHost::GetPlugin(const nsACString &aMimeType,
nsNPAPIPlugin** aPlugin)
{
nsresult rv = NS_ERROR_FAILURE;
*aPlugin = nullptr;
if (!aMimeType)
return NS_ERROR_ILLEGAL_VALUE;
// If plugins haven't been scanned yet, do so now
LoadPlugins();
nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
nsPluginTag* pluginTag = FindNativePluginForType(aMimeType, true);
if (pluginTag) {
rv = NS_OK;
PLUGIN_LOG(PLUGIN_LOG_BASIC,
("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n",
aMimeType, pluginTag->mFileName.get()));
PromiseFlatCString(aMimeType).get(), pluginTag->mFileName.get()));
#ifdef DEBUG
if (aMimeType && !pluginTag->mFileName.IsEmpty())
printf("For %s found plugin %s\n", aMimeType, pluginTag->mFileName.get());
if (!pluginTag->mFileName.IsEmpty())
printf("For %s found plugin %s\n",
PromiseFlatCString(aMimeType).get(), pluginTag->mFileName.get());
#endif
rv = EnsurePluginLoaded(pluginTag);
@ -1398,8 +1362,8 @@ nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("nsPluginHost::GetPlugin End mime=%s, rv=%d, plugin=%p name=%s\n",
aMimeType, rv, *aPlugin,
(pluginTag ? pluginTag->mFileName.get() : "(not found)")));
PromiseFlatCString(aMimeType).get(), rv, *aPlugin,
(pluginTag ? pluginTag->mFileName.get() : "(not found)")));
return rv;
}
@ -1618,6 +1582,7 @@ nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
return NS_ERROR_NOT_AVAILABLE;
}
// FIXME-jsplugins audit casts
nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
// We only ensure support for clearing Flash site data for now.
@ -2000,7 +1965,9 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
return NS_ERROR_OUT_OF_MEMORY;
pluginTag->mLibrary = library;
uint32_t state = pluginTag->GetBlocklistState();
uint32_t state;
rv = pluginTag->GetBlocklistState(&state);
NS_ENSURE_SUCCESS(rv, rv);
// If the blocklist says it is risky and we have never seen this
// plugin before, then disable it.
@ -2027,7 +1994,7 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
*aPluginsChanged = true;
}
// Avoid adding different versions of the same plugin if they are running
// Avoid adding different versions of the same plugin if they are running
// in-process, otherwise we risk undefined behaviour.
if (!nsNPAPIPlugin::RunPluginOOP(pluginTag)) {
if (HaveSamePlugin(pluginTag)) {
@ -2454,7 +2421,7 @@ nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
}
}
nsresult
void
nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
@ -2465,7 +2432,7 @@ nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
if (!aPluginTag) {
return NS_OK;
return;
}
// Update types with category manager
@ -2477,8 +2444,8 @@ nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
if (IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
shouldRegister = ePluginUnregister;
} else {
nsPluginTag *plugin = FindPluginForType(aPluginTag->mMimeTypes[i].get(),
true);
nsPluginTag *plugin = FindNativePluginForType(aPluginTag->mMimeTypes[i],
true);
shouldRegister = plugin ? ePluginRegister : ePluginUnregister;
}
@ -2489,13 +2456,6 @@ nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
mozilla::services::GetObserverService();
if (obsService)
obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr);
// Reload instances if needed
if (aPluginTag->IsActive()) {
return NS_OK;
}
return NS_OK;
}
/* static */ bool
@ -3089,7 +3049,7 @@ nsresult nsPluginHost::NewPluginURLStream(const nsString& aURL,
if (aURL.Length() <= 0)
return NS_OK;
// get the base URI for the plugin to create an absolute url
// get the base URI for the plugin to create an absolute url
// in case aURL is relative
nsRefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
if (owner) {
@ -3355,7 +3315,7 @@ nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance)
oldestInstance->Destroy();
mInstances.RemoveElement(oldestInstance);
// TODO: Remove this check once bug 752422 was investigated
if (pluginTag) {
if (pluginTag) {
OnPluginInstanceDestroyed(pluginTag);
}
}
@ -3365,7 +3325,7 @@ nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance)
aInstance->Destroy();
mInstances.RemoveElement(aInstance);
// TODO: Remove this check once bug 752422 was investigated
if (pluginTag) {
if (pluginTag) {
OnPluginInstanceDestroyed(pluginTag);
}
}
@ -3919,7 +3879,7 @@ nsPluginHost::InstanceArray()
return &mInstances;
}
void
void
nsPluginHost::DestroyRunningInstances(nsPluginTag* aPluginTag)
{
for (int32_t i = mInstances.Length(); i > 0; i--) {

View File

@ -75,7 +75,9 @@ class nsPluginHost MOZ_FINAL : public nsIPluginHost,
public nsITimerCallback,
public nsSupportsWeakReference
{
friend class nsPluginTag;
virtual ~nsPluginHost();
public:
nsPluginHost();
@ -88,16 +90,26 @@ public:
NS_DECL_NSIOBSERVER
NS_DECL_NSITIMERCALLBACK
nsresult Init();
nsresult LoadPlugins();
nsresult UnloadPlugins();
nsresult SetUpPluginInstance(const char *aMimeType,
nsresult SetUpPluginInstance(const nsACString &aMimeType,
nsIURI *aURL,
nsPluginInstanceOwner *aOwner);
bool PluginExistsForType(const char* aMimeType);
nsresult IsPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
// Acts like a bitfield
enum PluginFilter {
eExcludeNone = nsIPluginHost::EXCLUDE_NONE,
eExcludeDisabled = nsIPluginHost::EXCLUDE_DISABLED
};
// FIXME-jsplugins comment about fake
bool HavePluginForType(const nsACString & aMimeType,
PluginFilter aFilter = eExcludeDisabled);
// FIXME-jsplugins what if fake has different extensions
bool HavePluginForExtension(const nsACString & aExtension,
/* out */ nsACString & aMimeType,
PluginFilter aFilter = eExcludeDisabled);
void GetPlugins(nsTArray<nsRefPtr<nsPluginTag> >& aPluginArray);
void FindPluginsForContent(uint32_t aPluginEpoch,
@ -126,35 +138,39 @@ public:
nsresult FindProxyForURL(const char* url, char* *result);
nsresult UserAgent(const char **retstring);
nsresult ParsePostBufferToFixHeaders(const char *inPostData, uint32_t inPostDataLen,
char **outPostData, uint32_t *outPostDataLen);
nsresult ParsePostBufferToFixHeaders(const char *inPostData,
uint32_t inPostDataLen,
char **outPostData,
uint32_t *outPostDataLen);
nsresult CreateTempFileToPost(const char *aPostDataURL, nsIFile **aTmpFile);
nsresult NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow);
void AddIdleTimeTarget(nsIPluginInstanceOwner* objectFrame, bool isVisible);
void RemoveIdleTimeTarget(nsIPluginInstanceOwner* objectFrame);
nsresult GetPluginName(nsNPAPIPluginInstance *aPluginInstance, const char** aPluginName);
nsresult GetPluginName(nsNPAPIPluginInstance *aPluginInstance,
const char** aPluginName);
nsresult StopPluginInstance(nsNPAPIPluginInstance* aInstance);
nsresult GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance, nsIPluginTag **aPluginTag);
nsresult GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance,
nsIPluginTag **aPluginTag);
nsresult
NewPluginURLStream(const nsString& aURL,
nsNPAPIPluginInstance *aInstance,
NewPluginURLStream(const nsString& aURL,
nsNPAPIPluginInstance *aInstance,
nsNPAPIPluginStreamListener *aListener,
nsIInputStream *aPostStream = nullptr,
const char *aHeadersData = nullptr,
const char *aHeadersData = nullptr,
uint32_t aHeadersDataLen = 0);
nsresult
GetURLWithHeaders(nsNPAPIPluginInstance *pluginInst,
const char* url,
GetURLWithHeaders(nsNPAPIPluginInstance *pluginInst,
const char* url,
const char* target = nullptr,
nsNPAPIPluginStreamListener* streamListener = nullptr,
const char* altHost = nullptr,
const char* referrer = nullptr,
bool forceJSEnabled = false,
uint32_t getHeadersLength = 0,
uint32_t getHeadersLength = 0,
const char* getHeaders = nullptr);
nsresult
@ -162,15 +178,11 @@ public:
const char* aURL);
nsresult
AddHeadersToChannel(const char *aHeadersData, uint32_t aHeadersDataLen,
AddHeadersToChannel(const char *aHeadersData, uint32_t aHeadersDataLen,
nsIChannel *aGenericChannel);
static nsresult GetPluginTempDir(nsIFile **aDir);
// Writes updated plugins settings to disk and unloads the plugin
// if it is now disabled
nsresult UpdatePluginInfo(nsPluginTag* aPluginTag);
// Helper that checks if a type is whitelisted in plugin.allowed_types.
// Always returns true if plugin.allowed_types is not set
static bool IsTypeWhitelisted(const char *aType);
@ -202,14 +214,12 @@ public:
nsTArray< nsRefPtr<nsNPAPIPluginInstance> > *InstanceArray();
void DestroyRunningInstances(nsPluginTag* aPluginTag);
// Return the tag for |aLibrary| if found, nullptr if not.
nsPluginTag* FindTagForLibrary(PRLibrary* aLibrary);
// The last argument should be false if we already have an in-flight stream
// and don't need to set up a new stream.
nsresult InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
nsresult InstantiatePluginInstance(const nsACString& aMimeType, nsIURI* aURL,
nsObjectLoadingContent *aContent,
nsPluginInstanceOwner** aOwner);
@ -218,7 +228,7 @@ public:
nsPluginTag* PluginWithId(uint32_t aId);
nsresult GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin);
nsresult GetPlugin(const nsACString &aMimeType, nsNPAPIPlugin** aPlugin);
nsresult GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin);
void NotifyContentModuleDestroyed(uint32_t aPluginId);
@ -231,19 +241,27 @@ public:
private:
friend class nsPluginUnloadRunnable;
nsresult
TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsPluginInstanceOwner *aOwner);
void DestroyRunningInstances(nsPluginTag* aPluginTag);
// Writes updated plugins settings to disk and unloads the plugin
// if it is now disabled. Should only be called by the plugin tag in question
void UpdatePluginInfo(nsPluginTag* aPluginTag);
nsresult TrySetUpPluginInstance(const nsACString &aMimeType, nsIURI *aURL,
nsPluginInstanceOwner *aOwner);
// FIXME-jsplugins comment here about when things may be fake
nsPluginTag*
FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches);
// Return an nsPluginTag for this type, if any. If aCheckEnabled is
// true, only enabled plugins will be returned.
nsPluginTag*
FindPluginForType(const char* aMimeType, bool aCheckEnabled);
nsPluginTag* FindNativePluginForType(const nsACString & aMimeType,
bool aCheckEnabled);
nsPluginTag*
FindPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
nsPluginTag* FindNativePluginForExtension(const nsACString & aExtension,
/* out */ nsACString & aMimeType,
bool aCheckEnabled);
nsresult
FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner);
@ -253,6 +271,7 @@ private:
nsresult
FindPlugins(bool aCreatePluginList, bool * aPluginsChanged);
// FIXME revisit, no ns prefix
// Registers or unregisters the given mime type with the category manager
// (performs no checks - see UpdateCategoryManager)
enum nsRegisterType { ePluginRegister, ePluginUnregister };
@ -287,10 +306,10 @@ private:
// Checks to see if a tag object is in our list of live tags.
bool IsLiveTag(nsIPluginTag* tag);
// Checks our list of live tags for an equivalent tag.
nsPluginTag* HaveSamePlugin(const nsPluginTag * aPluginTag);
// Returns the first plugin at |path|
nsPluginTag* FirstPluginWithPath(const nsCString& path);

View File

@ -15,6 +15,7 @@
#include "nsIPlatformCharset.h"
#include "nsPluginLogging.h"
#include "nsNPAPIPlugin.h"
#include "nsCharSeparatedTokenizer.h"
#include "mozilla/Preferences.h"
#include "mozilla/unused.h"
#include <cctype>
@ -34,15 +35,65 @@ using namespace mozilla;
static const char kPrefDefaultEnabledState[] = "plugin.default.state";
static const char kPrefDefaultEnabledStateXpi[] = "plugin.defaultXpi.state";
inline char* new_str(const char* str)
// check comma delimited extensions
static bool ExtensionInList(const nsCString& aExtensionList,
const nsACString& aExtension)
{
if (str == nullptr)
return nullptr;
char* result = new char[strlen(str) + 1];
if (result != nullptr)
return strcpy(result, str);
return result;
nsCCharSeparatedTokenizer extensions(aExtensionList, ',');
while (extensions.hasMoreTokens()) {
const nsCSubstring& extension = extensions.nextToken();
if (extension.Equals(aExtension, nsCaseInsensitiveCStringComparator())) {
return true;
}
}
return false;
}
// Search for an extension in an extensions array, and return its
// matching mime type
static bool SearchExtensions(const nsTArray<nsCString> & aExtensions,
const nsTArray<nsCString> & aMimeTypes,
const nsACString & aFindExtension,
nsACString & aMatchingType)
{
uint32_t mimes = aMimeTypes.Length();
MOZ_ASSERT(mimes == aExtensions.Length(),
"These arrays should have matching elements");
aMatchingType.Truncate();
for (uint32_t i = 0; i < mimes; i++) {
if (ExtensionInList(aExtensions[i], aFindExtension)) {
aMatchingType = aMimeTypes[i];
return true;
}
}
return false;
}
static nsCString
MakeNiceFileName(const nsCString & aFileName)
{
nsCString niceName = aFileName;
int32_t niceNameLength = aFileName.RFind(".");
NS_ASSERTION(niceNameLength != kNotFound, "aFileName doesn't have a '.'?");
while (niceNameLength > 0) {
char chr = aFileName[niceNameLength - 1];
if (!std::isalpha(chr))
niceNameLength--;
else
break;
}
// If it turns out that niceNameLength <= 0, we'll fall back and use the
// entire aFileName (which we've already taken care of, a few lines back).
if (niceNameLength > 0) {
niceName.Truncate(niceNameLength);
}
ToLowerCase(niceName);
return niceName;
}
static nsCString
@ -58,6 +109,27 @@ MakePrefNameForPlugin(const char* const subname, nsPluginTag* aTag)
return pref;
}
static nsresult
CStringArrayToXPCArray(nsTArray<nsCString> & aArray,
uint32_t* aCount,
char16_t*** aResults)
{
uint32_t count = aArray.Length();
if (!count) {
return NS_ERROR_NOT_AVAILABLE;
}
*aResults =
static_cast<char16_t**>(nsMemory::Alloc(count * sizeof(**aResults)));
*aCount = count;
for (uint32_t i = 0; i < count; i++) {
(*aResults)[i] = ToNewUnicode(NS_ConvertUTF8toUTF16(aArray[i]));
}
return NS_OK;
}
static nsCString
GetStatePrefNameForPlugin(nsPluginTag* aTag)
{
@ -82,7 +154,6 @@ nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo,
mFullPath(aPluginInfo->fFullPath),
mVersion(aPluginInfo->fVersion),
mLastModifiedTime(aLastModifiedTime),
mNiceFileName(),
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
mCachedBlocklistStateValid(false),
mIsFromExtension(fromExtension)
@ -118,7 +189,6 @@ nsPluginTag::nsPluginTag(const char* aName,
mFullPath(aFullPath),
mVersion(aVersion),
mLastModifiedTime(aLastModifiedTime),
mNiceFileName(),
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
mCachedBlocklistStateValid(false),
mIsFromExtension(fromExtension)
@ -266,7 +336,7 @@ static nsresult ConvertToUTF8(nsIUnicodeDecoder *aUnicodeDecoder,
NS_ENSURE_SUCCESS(rv, rv);
buffer.SetLength(outUnicodeLen);
CopyUTF16toUTF8(buffer, aString);
return NS_OK;
}
#endif
@ -277,7 +347,7 @@ nsresult nsPluginTag::EnsureMembersAreUTF8()
return NS_OK;
#else
nsresult rv;
nsCOMPtr<nsIPlatformCharset> pcs =
do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
@ -359,6 +429,13 @@ nsPluginTag::IsActive()
return IsEnabled() && !IsBlocklisted();
}
NS_IMETHODIMP
nsPluginTag::GetActive(bool *aResult)
{
*aResult = IsActive();
return NS_OK;
}
bool
nsPluginTag::IsEnabled()
{
@ -376,7 +453,9 @@ nsPluginTag::GetDisabled(bool* aDisabled)
bool
nsPluginTag::IsBlocklisted()
{
return GetBlocklistState() == nsIBlocklistService::STATE_BLOCKED;
uint32_t blocklistState;
nsresult rv = GetBlocklistState(&blocklistState);
return NS_FAILED(rv) || blocklistState == nsIBlocklistService::STATE_BLOCKED;
}
NS_IMETHODIMP
@ -476,52 +555,19 @@ nsPluginTag::SetPluginState(PluginState state)
NS_IMETHODIMP
nsPluginTag::GetMimeTypes(uint32_t* aCount, char16_t*** aResults)
{
uint32_t count = mMimeTypes.Length();
*aResults = static_cast<char16_t**>
(nsMemory::Alloc(count * sizeof(**aResults)));
if (!*aResults)
return NS_ERROR_OUT_OF_MEMORY;
*aCount = count;
for (uint32_t i = 0; i < count; i++) {
(*aResults)[i] = ToNewUnicode(NS_ConvertUTF8toUTF16(mMimeTypes[i]));
}
return NS_OK;
return CStringArrayToXPCArray(mMimeTypes, aCount, aResults);
}
NS_IMETHODIMP
nsPluginTag::GetMimeDescriptions(uint32_t* aCount, char16_t*** aResults)
{
uint32_t count = mMimeDescriptions.Length();
*aResults = static_cast<char16_t**>
(nsMemory::Alloc(count * sizeof(**aResults)));
if (!*aResults)
return NS_ERROR_OUT_OF_MEMORY;
*aCount = count;
for (uint32_t i = 0; i < count; i++) {
(*aResults)[i] = ToNewUnicode(NS_ConvertUTF8toUTF16(mMimeDescriptions[i]));
}
return NS_OK;
return CStringArrayToXPCArray(mMimeDescriptions, aCount, aResults);
}
NS_IMETHODIMP
nsPluginTag::GetExtensions(uint32_t* aCount, char16_t*** aResults)
{
uint32_t count = mExtensions.Length();
*aResults = static_cast<char16_t**>
(nsMemory::Alloc(count * sizeof(**aResults)));
if (!*aResults)
return NS_ERROR_OUT_OF_MEMORY;
*aCount = count;
for (uint32_t i = 0; i < count; i++) {
(*aResults)[i] = ToNewUnicode(NS_ConvertUTF8toUTF16(mExtensions[i]));
}
return NS_OK;
return CStringArrayToXPCArray(mExtensions, aCount, aResults);
}
bool
@ -571,27 +617,17 @@ nsCString nsPluginTag::GetNiceFileName() {
return mNiceFileName;
}
mNiceFileName.Assign(mFileName);
int32_t niceNameLength = mFileName.RFind(".");
NS_ASSERTION(niceNameLength != kNotFound, "mFileName doesn't have a '.'?");
while (niceNameLength > 0) {
char chr = mFileName[niceNameLength - 1];
if (!std::isalpha(chr))
niceNameLength--;
else
break;
}
// If it turns out that niceNameLength <= 0, we'll fall back and use the
// entire mFileName (which we've already taken care of, a few lines back)
if (niceNameLength > 0) {
mNiceFileName.Truncate(niceNameLength);
}
ToLowerCase(mNiceFileName);
mNiceFileName = MakeNiceFileName(mFileName);
return mNiceFileName;
}
NS_IMETHODIMP
nsPluginTag::GetNiceName(nsACString & aResult)
{
aResult = GetNiceFileName();
return NS_OK;
}
void nsPluginTag::ImportFlagsToPrefs(uint32_t flags)
{
if (!(flags & NS_PLUGIN_FLAG_ENABLED)) {
@ -599,16 +635,20 @@ void nsPluginTag::ImportFlagsToPrefs(uint32_t flags)
}
}
uint32_t
nsPluginTag::GetBlocklistState()
NS_IMETHODIMP
nsPluginTag::GetBlocklistState(uint32_t *aResult)
{
if (mCachedBlocklistStateValid) {
return mCachedBlocklistState;
*aResult = mCachedBlocklistState;
return NS_OK;
}
nsCOMPtr<nsIBlocklistService> blocklist = do_GetService("@mozilla.org/extensions/blocklist;1");
nsCOMPtr<nsIBlocklistService> blocklist =
do_GetService("@mozilla.org/extensions/blocklist;1");
if (!blocklist) {
return nsIBlocklistService::STATE_NOT_BLOCKED;
*aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
return NS_OK;
}
// The EmptyString()s are so we use the currently running application
@ -616,13 +656,29 @@ nsPluginTag::GetBlocklistState()
uint32_t state;
if (NS_FAILED(blocklist->GetPluginBlocklistState(this, EmptyString(),
EmptyString(), &state))) {
return nsIBlocklistService::STATE_NOT_BLOCKED;
*aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
return NS_OK;
}
MOZ_ASSERT(state <= UINT16_MAX);
mCachedBlocklistState = (uint16_t) state;
mCachedBlocklistStateValid = true;
return state;
*aResult = state;
return NS_OK;
}
bool
nsPluginTag::HasMimeType(const nsACString & aMimeType) const
{
return mMimeTypes.Contains(aMimeType,
nsCaseInsensitiveCStringArrayComparator());
}
bool
nsPluginTag::HasExtension(const nsACString & aExtension,
nsACString & aMatchingType) const
{
return SearchExtensions(mExtensions, mMimeTypes, aExtension, aMatchingType);
}
void

View File

@ -106,9 +106,17 @@ public:
int64_t mLastModifiedTime;
nsCOMPtr<nsITimer> mUnloadTimer;
uint32_t GetBlocklistState();
void InvalidateBlocklistState();
// Returns true if this plugin claims it supports this MIME type. The
// comparison is done ASCII-case-insensitively.
bool HasMimeType(const nsACString & aMimeType) const;
// Returns true if this plugin claims it supports the given extension. In hat
// case, aMatchingType is set to the MIME type the plugin claims corresponds
// to this extension. Again, the extension is done ASCII-case-insensitively.
bool HasExtension(const nsACString & aExtension,
/* out */ nsACString & aMatchingType) const;
private:
virtual ~nsPluginTag();

View File

@ -417,7 +417,9 @@ PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId,
return nullptr;
}
parent->mIsFlashPlugin = aPluginTag->mIsFlashPlugin;
parent->mIsBlocklisted = aPluginTag->GetBlocklistState() != 0;
uint32_t blocklistState;
nsresult rv = aPluginTag->GetBlocklistState(&blocklistState);
parent->mIsBlocklisted = NS_FAILED(rv) || blocklistState != 0;
if (!parent->mIsStartingAsync) {
int32_t launchTimeoutSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
if (!parent->mSubprocess->WaitUntilConnected(launchTimeoutSecs * 1000)) {

View File

@ -283,6 +283,32 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Promise)
if (tmp->IsBlack()) {
if (tmp->mResult.isObject()) {
JS::ExposeObjectToActiveJS(&(tmp->mResult.toObject()));
}
if (tmp->mAllocationStack) {
JS::ExposeObjectToActiveJS(tmp->mAllocationStack);
}
if (tmp->mRejectionStack) {
JS::ExposeObjectToActiveJS(tmp->mRejectionStack);
}
if (tmp->mFullfillmentStack) {
JS::ExposeObjectToActiveJS(tmp->mFullfillmentStack);
}
return true;
}
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Promise)
return tmp->IsBlackAndDoesNotNeedTracing(tmp);
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Promise)
return tmp->IsBlack();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Promise)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Promise)

View File

@ -75,7 +75,7 @@ class Promise : public nsISupports,
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROMISE_IID)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise)
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(Promise)
MOZ_DECLARE_REFCOUNTED_TYPENAME(Promise)
// Promise creation tries to create a JS reflector for the Promise, so is

View File

@ -32,6 +32,7 @@ interface ServiceWorkerContainer : EventTarget {
attribute EventHandler oncontrollerchange;
attribute EventHandler onreloadpage;
attribute EventHandler onerror;
attribute EventHandler onmessage;
};
// Testing only.

View File

@ -140,8 +140,6 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget {
[ChromeOnly, Exposed=Window]
readonly attribute MozChannel? channel;
[Throws]
void sendAsBinary(DOMString body);
[Throws, ChromeOnly, Exposed=Window]
any getInterface(IID iid);

View File

@ -5,6 +5,7 @@
*/
#include "ServiceWorkerClient.h"
#include "ServiceWorkerContainer.h"
#include "mozilla/dom/MessageEvent.h"
#include "nsGlobalWindow.h"
@ -59,16 +60,23 @@ public:
return NS_ERROR_FAILURE;
}
ErrorResult result;
dom::Navigator* navigator = window->GetNavigator(result);
if (NS_WARN_IF(result.Failed())) {
return result.ErrorCode();
}
nsRefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
AutoJSAPI jsapi;
jsapi.Init(window);
JSContext* cx = jsapi.cx();
return DispatchDOMEvent(cx, window);
return DispatchDOMEvent(cx, container);
}
private:
NS_IMETHOD
DispatchDOMEvent(JSContext* aCx, nsGlobalWindow* aTargetWindow)
DispatchDOMEvent(JSContext* aCx, ServiceWorkerContainer* aTargetContainer)
{
AssertIsOnMainThread();
@ -84,7 +92,7 @@ private:
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMMessageEvent> event = new MessageEvent(aTargetWindow,
nsCOMPtr<nsIDOMMessageEvent> event = new MessageEvent(aTargetContainer,
nullptr, nullptr);
nsresult rv =
event->InitMessageEvent(NS_LITERAL_STRING("message"),
@ -101,7 +109,7 @@ private:
event->SetTrusted(true);
bool status = false;
aTargetWindow->DispatchEvent(event, &status);
aTargetContainer->DispatchEvent(event, &status);
if (!status) {
return NS_ERROR_FAILURE;

View File

@ -31,6 +31,7 @@ public:
IMPL_EVENT_HANDLER(controllerchange)
IMPL_EVENT_HANDLER(reloadpage)
IMPL_EVENT_HANDLER(error)
IMPL_EVENT_HANDLER(message)
explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow);

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