mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 00:35:44 +00:00
Merge m-c to b2g-inbound a=merge
This commit is contained in:
commit
cab7aa8023
@ -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*
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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");
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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));
|
||||
|
@ -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) {
|
||||
|
@ -129,6 +129,8 @@ function updateUI() {
|
||||
warningsNode.appendChild(li);
|
||||
}
|
||||
}
|
||||
|
||||
AppManager.update("details");
|
||||
}
|
||||
|
||||
function showPrepackageLog() {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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(/^\/|\\/, "");
|
||||
}
|
||||
|
@ -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 + "'");
|
||||
})
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -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");
|
||||
|
@ -113,6 +113,7 @@
|
||||
return Task.spawn(function*() {
|
||||
let packagedAppLocation = getTestFilePath("app");
|
||||
yield win.Cmds.importPackagedApp(packagedAppLocation);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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 },
|
||||
|
130
browser/extensions/shumway/chrome/pingpong.js
Normal file
130
browser/extensions/shumway/chrome/pingpong.js
Normal 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;
|
||||
})();
|
@ -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>
|
||||
|
90
browser/extensions/shumway/chrome/viewerDebugger.js
Normal file
90
browser/extensions/shumway/chrome/viewerDebugger.js
Normal 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
|
||||
};
|
||||
})();
|
@ -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 {
|
||||
|
@ -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
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,2 +1,2 @@
|
||||
0.9.3775
|
||||
a82ac47
|
||||
0.10.182
|
||||
0195a96
|
||||
|
BIN
browser/extensions/shumway/content/web/noflv.mp4
Normal file
BIN
browser/extensions/shumway/content/web/noflv.mp4
Normal file
Binary file not shown.
@ -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>
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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">
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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>
|
@ -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'
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -41,6 +41,7 @@ ProcessGlobal::Get()
|
||||
NS_IMETHODIMP_(bool)
|
||||
ProcessGlobal::MarkForCC()
|
||||
{
|
||||
MarkScopesForCC();
|
||||
return mMessageManager ? mMessageManager->MarkForCC() : false;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -2020,7 +2020,7 @@ public:
|
||||
};
|
||||
|
||||
static already_AddRefed<nsIDocumentLoaderFactory>
|
||||
FindInternalContentViewer(const char* aType,
|
||||
FindInternalContentViewer(const nsACString& aType,
|
||||
ContentViewerType* aLoaderType = nullptr);
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -401,6 +401,8 @@ public:
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mGlobal;
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
void MarkScopesForCC();
|
||||
protected:
|
||||
friend class nsMessageManagerScriptCx;
|
||||
nsMessageManagerScriptExecutor() { MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor); }
|
||||
|
@ -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.
|
||||
|
@ -129,6 +129,7 @@ nsInProcessTabChildGlobal::~nsInProcessTabChildGlobal()
|
||||
NS_IMETHODIMP_(bool)
|
||||
nsInProcessTabChildGlobal::MarkForCC()
|
||||
{
|
||||
MarkScopesForCC();
|
||||
return mMessageManager ? mMessageManager->MarkForCC() : false;
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -488,7 +488,6 @@ public:
|
||||
}
|
||||
aRv = Send(RequestBody(aStream));
|
||||
}
|
||||
void SendAsBinary(const nsAString& aBody, ErrorResult& aRv);
|
||||
|
||||
void Abort();
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -3202,6 +3202,9 @@ NS_IMPL_RELEASE_INHERITED(TabChildGlobal, DOMEventTargetHelper)
|
||||
NS_IMETHODIMP_(bool)
|
||||
TabChildGlobal::MarkForCC()
|
||||
{
|
||||
if (mTabChild) {
|
||||
mTabChild->MarkScopesForCC();
|
||||
}
|
||||
return mMessageManager ? mMessageManager->MarkForCC() : false;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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]
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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>
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ support-files =
|
||||
webaudio.js
|
||||
|
||||
[test_analyserNode.html]
|
||||
[test_analyserScale.html]
|
||||
[test_analyserNodeOutput.html]
|
||||
[test_analyserNodePassThrough.html]
|
||||
[test_AudioBuffer.html]
|
||||
|
59
dom/media/webaudio/test/test_analyserScale.html
Normal file
59
dom/media/webaudio/test/test_analyserScale.html
Normal 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>
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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--) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -32,6 +32,7 @@ interface ServiceWorkerContainer : EventTarget {
|
||||
attribute EventHandler oncontrollerchange;
|
||||
attribute EventHandler onreloadpage;
|
||||
attribute EventHandler onerror;
|
||||
attribute EventHandler onmessage;
|
||||
};
|
||||
|
||||
// Testing only.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user