Merge mozilla-central into services-central

This commit is contained in:
Gregory Szorc 2013-04-23 15:55:02 -07:00
commit c172cb0b78
409 changed files with 9367 additions and 5957 deletions

View File

@ -48,6 +48,7 @@ enum AccType {
* Other accessible types.
*/
eApplicationType,
eHTMLOptGroupType,
eImageMapType,
eMenuPopupType,
eProgressType,

View File

@ -488,6 +488,8 @@ public:
bool IsHTMLListItem() const { return mType == eHTMLLiType; }
HTMLLIAccessible* AsHTMLListItem();
bool IsHTMLOptGroup() const { return mType == eHTMLOptGroupType; }
bool IsHTMLTable() const { return mType == eHTMLTableType; }
bool IsHTMLTableRow() const { return mType == eHTMLTableRowType; }

View File

@ -1748,25 +1748,6 @@ DocAccessible::UpdateTree(Accessible* aContainer, nsIContent* aChildNode,
if (child) {
updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
// XXX: since select change insertion point of option contained by optgroup
// then we need to have special processing for them (bug 690417).
if (!aIsInsert && aChildNode->IsHTML(nsGkAtoms::optgroup) &&
aContainer->GetContent() &&
aContainer->GetContent()->IsHTML(nsGkAtoms::select)) {
for (nsIContent* optContent = aChildNode->GetFirstChild(); optContent;
optContent = optContent->GetNextSibling()) {
if (optContent->IsHTML(nsGkAtoms::option)) {
Accessible* option = GetAccessible(optContent);
if (option) {
NS_ASSERTION(option->Parent() == aContainer,
"Not expected hierarchy on HTML select!");
if (option->Parent() == aContainer)
updateFlags |= UpdateTreeInternal(option, aIsInsert, reorderEvent);
}
}
}
}
} else {
TreeWalker walker(aContainer, aChildNode, true);

View File

@ -1829,8 +1829,10 @@ void
HyperTextAccessible::GetSelectionDOMRanges(int16_t aType,
nsTArray<nsRange*>* aRanges)
{
// Ignore selection if it is not visible.
nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
if (!frameSelection)
if (!frameSelection ||
frameSelection->GetDisplaySelection() <= nsISelectionController::SELECTION_HIDDEN)
return;
Selection* domSel = frameSelection->GetSelection(aType);

View File

@ -125,16 +125,7 @@ HTMLSelectListAccessible::CacheChildren()
// as well as the accessibles for them. Avoid whitespace text nodes. We want
// to count all the <optgroup>s and <option>s as children because we want
// a flat tree under the Select List.
CacheOptSiblings(mContent);
}
////////////////////////////////////////////////////////////////////////////////
// HTMLSelectListAccessible protected
void
HTMLSelectListAccessible::CacheOptSiblings(nsIContent* aParentContent)
{
for (nsIContent* childContent = aParentContent->GetFirstChild(); childContent;
for (nsIContent* childContent = mContent->GetFirstChild(); childContent;
childContent = childContent->GetNextSibling()) {
if (!childContent->IsHTML()) {
continue;
@ -149,10 +140,6 @@ HTMLSelectListAccessible::CacheOptSiblings(nsIContent* aParentContent)
GetAccService()->GetOrCreateAccessible(childContent, this);
if (accessible)
AppendChild(accessible);
// Deep down into optgroup element.
if (tag == nsGkAtoms::optgroup)
CacheOptSiblings(childContent);
}
}
}
@ -174,7 +161,7 @@ HTMLSelectOptionAccessible::
role
HTMLSelectOptionAccessible::NativeRole()
{
if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
if (GetCombobox())
return roles::COMBOBOX_OPTION;
return roles::OPTION;
@ -333,23 +320,21 @@ HTMLSelectOptionAccessible::SetSelected(bool aSelect)
Accessible*
HTMLSelectOptionAccessible::ContainerWidget() const
{
return mParent && mParent->IsListControl() ? mParent : nullptr;
Accessible* parent = Parent();
if (parent && parent->IsHTMLOptGroup())
parent = parent->Parent();
return parent && parent->IsListControl() ? parent : nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// HTMLSelectOptGroupAccessible
////////////////////////////////////////////////////////////////////////////////
HTMLSelectOptGroupAccessible::
HTMLSelectOptGroupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
HTMLSelectOptionAccessible(aContent, aDoc)
{
}
role
HTMLSelectOptGroupAccessible::NativeRole()
{
return roles::HEADING;
return roles::GROUPING;
}
uint64_t
@ -376,20 +361,6 @@ HTMLSelectOptGroupAccessible::ActionCount()
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// HTMLSelectOptGroupAccessible: Accessible protected
void
HTMLSelectOptGroupAccessible::CacheChildren()
{
// XXX To do (bug 378612) - create text child for the anonymous attribute
// content, so that nsIAccessibleText is supported for the <optgroup> as it is
// for an <option>. Attribute content is what layout creates for
// the label="foo" on the <optgroup>. See eStyleContentType_Attr and
// CreateAttributeContent() in nsCSSFrameConstructor
}
////////////////////////////////////////////////////////////////////////////////
// HTMLComboboxAccessible
////////////////////////////////////////////////////////////////////////////////

View File

@ -57,13 +57,6 @@ protected:
// Accessible
virtual void CacheChildren();
// HTMLSelectListAccessible
/**
* Recursive helper for CacheChildren().
*/
void CacheOptSiblings(nsIContent* aParentContent);
};
/*
@ -107,8 +100,12 @@ private:
*/
Accessible* GetSelect() const
{
if (mParent && mParent->IsListControl()) {
Accessible* combobox = mParent->Parent();
Accessible* parent = mParent;
if (parent && parent->IsHTMLOptGroup())
parent = parent->Parent();
if (parent && parent->IsListControl()) {
Accessible* combobox = parent->Parent();
return combobox && combobox->IsCombobox() ? combobox : mParent.get();
}
@ -120,8 +117,12 @@ private:
*/
Accessible* GetCombobox() const
{
if (mParent && mParent->IsListControl()) {
Accessible* combobox = mParent->Parent();
Accessible* parent = mParent;
if (parent && parent->IsHTMLOptGroup())
parent = parent->Parent();
if (parent && parent->IsListControl()) {
Accessible* combobox = parent->Parent();
return combobox && combobox->IsCombobox() ? combobox : nullptr;
}
@ -136,7 +137,9 @@ class HTMLSelectOptGroupAccessible : public HTMLSelectOptionAccessible
{
public:
HTMLSelectOptGroupAccessible(nsIContent* aContent, DocAccessible* aDoc);
HTMLSelectOptGroupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
HTMLSelectOptionAccessible(aContent, aDoc)
{ mType = eHTMLOptGroupType; }
virtual ~HTMLSelectOptGroupAccessible() {}
// nsIAccessible
@ -149,10 +152,6 @@ public:
// ActionAccessible
virtual uint8_t ActionCount();
protected:
// Accessible
virtual void CacheChildren();
};
/** ------------------------------------------------------ */

View File

@ -9,8 +9,9 @@
#include "nsCOMPtr.h"
#include "nsObjCExceptions.h"
#include "nsIFrame.h"
#include "nsView.h"
#include "nsIWidget.h"
#include "nsViewManager.h"
using namespace mozilla::a11y;

View File

@ -57,6 +57,10 @@
gQueue.push(new takeFocusInvoker("plugin"));
gQueue.push(new takeFocusInvoker(document));
gQueue.push(new takeFocusInvoker("lb_item2"));
gQueue.push(new takeFocusInvoker(document));
gQueue.push(new takeFocusInvoker("lb_item3.2"));
gQueue.push(new takeFocusInvoker(document));
gQueue.push(new takeFocusInvoker("lb_item3.1"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
@ -114,6 +118,10 @@
<select id="listbox" size="5">
<option id="lb_item1">item1</option>
<option id="lb_item2">item2</option>
<optgroup>
<option id="lb_item3.1">item 3.1</option>
<option id="lb_item3.2">item 3.2</option>
</optgroup>
</select>
</body>
</html>

View File

@ -13,6 +13,7 @@ include $(DEPTH)/config/autoconf.mk
MOCHITEST_A11Y_FILES = \
test_general.html \
test_userinput.html \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,95 @@
<html>
<head>
<title>Text selection by user input</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
src="../states.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript">
/**
* Invokers
*/
function synthTabAndCheckPrevTabbed(aID, aPrevID)
{
this.__proto__ = new synthTab(aID, new focusChecker(aID));
this.finalCheck = function changeSelection_finalCheck()
{
var prevTabbed = getAccessible(aPrevID, [ nsIAccessibleText ]);
is(prevTabbed.selectionCount, 0,
"Wrong selection count for " + aPrevID);
var exceptionCaught = false;
try {
var startOffsetObj = {}, endOffsetObj = {};
prevTabbed.getSelectionBounds(0, startOffsetObj, endOffsetObj);
} catch (e) {
exceptionCaught = true;
}
ok(exceptionCaught, "No selection was expected for " + aPrevID);
}
this.getID = function changeSelection_getID()
{
return "Hidden selection check for " + aPrevID;
}
}
/**
* Do tests
*/
//gA11yEventDumpToConsole = true; // debug stuff
var gQueue = null;
function doTests()
{
gQueue = new eventQueue();
// Tab to 't2' and then tab out it: it must has no selection.
gQueue.push(new synthFocus("t1"));
gQueue.push(new synthTab("t2", new focusChecker("t2")));
gQueue.push(new synthTabAndCheckPrevTabbed("t3", "t2"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTests);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=440590"
title="Text selection information is not updated when HTML and XUL entries lose focus">
Bug 440590
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<input type="text" id="t1" maxlength="3" size="3" value="1">
<input type="text" id="t2" maxlength="3" size="3" value="1">
<input type="text" id="t3" maxlength="3" size="3" value="1">
</body>
</html>

View File

@ -20,21 +20,27 @@
role: ROLE_LISTBOX,
children: [
{
role: ROLE_HEADING
},
{
role: ROLE_OPTION,
role: ROLE_GROUPING,
children: [
{
role: ROLE_TEXT_LEAF
}
]
},
{
role: ROLE_OPTION,
children: [
role: ROLE_STATICTEXT,
children: [ ]
},
{
role: ROLE_TEXT_LEAF
role: ROLE_OPTION,
children: [
{
role: ROLE_TEXT_LEAF
}
]
},
{
role: ROLE_OPTION,
children: [
{
role: ROLE_TEXT_LEAF
}
]
}
]
},
@ -57,24 +63,30 @@
role: ROLE_COMBOBOX_LIST,
children: [
{
role: ROLE_HEADING
},
{
role: ROLE_COMBOBOX_OPTION,
role: ROLE_GROUPING,
children: [
{
role: ROLE_TEXT_LEAF
}
]
},
{
role: ROLE_COMBOBOX_OPTION,
children: [
role: ROLE_STATICTEXT,
children: [ ]
},
{
role: ROLE_TEXT_LEAF
}
role: ROLE_COMBOBOX_OPTION,
children: [
{
role: ROLE_TEXT_LEAF
}
]
},
{
role: ROLE_COMBOBOX_OPTION,
children: [
{
role: ROLE_TEXT_LEAF
}
]
},
]
},
},
{
role: ROLE_COMBOBOX_OPTION,
children: [

View File

@ -67,4 +67,7 @@ else if (isTabPBSupported) {
};
}
// Test disabled because of bug 855771
module.exports = {};
require('test').run(exports);

View File

@ -91,7 +91,7 @@ DirectoryProvider.prototype = {
if (!Services.volumeService) {
return false;
}
let volume = Services.volumeService.getVolumeByPath(volumePath);
let volume = Services.volumeService.createOrGetVolumeByPath(volumePath);
if (!volume || volume.state !== Ci.nsIVolume.STATE_MOUNTED) {
return false;
}
@ -201,7 +201,7 @@ DirectoryProvider.prototype = {
}
if (Services.volumeService) {
let extVolume = Services.volumeService.getVolumeByPath(path);
let extVolume = Services.volumeService.createOrGetVolumeByPath(path);
if (!extVolume) {
path = LOCAL_DIR;
}

View File

@ -918,9 +918,7 @@ pref("dom.ipc.plugins.enabled.x86_64", true);
pref("dom.ipc.plugins.enabled", true);
#endif
#ifdef MOZ_E10S_COMPAT
pref("browser.tabs.remote", true);
#endif
pref("browser.tabs.remote", false);
// This pref governs whether we attempt to work around problems caused by
// plugins using OS calls to manipulate the cursor while running out-of-
@ -1084,6 +1082,7 @@ pref("devtools.netmonitor.enabled", true);
// The default Network Monitor UI settings
pref("devtools.netmonitor.panes-network-details-width", 450);
pref("devtools.netmonitor.panes-network-details-height", 450);
// Enable the Tilt inspector
pref("devtools.tilt.enabled", true);
@ -1128,6 +1127,8 @@ pref("devtools.webconsole.filter.error", true);
pref("devtools.webconsole.filter.warn", true);
pref("devtools.webconsole.filter.info", true);
pref("devtools.webconsole.filter.log", true);
pref("devtools.webconsole.filter.secerror", true);
pref("devtools.webconsole.filter.secwarn", true);
// Text size in the Web Console. Use 0 for the system default size.
pref("devtools.webconsole.fontSize", 0);

View File

@ -40,6 +40,10 @@ var FullZoom = {
// Initialization & Destruction
init: function FullZoom_init() {
// Bug 691614 - zooming support for electrolysis
if (gMultiProcessBrowser)
return;
// Listen for scrollwheel events so we can save scrollwheel-based changes.
window.addEventListener("DOMMouseScroll", this, false);
@ -58,6 +62,10 @@ var FullZoom = {
},
destroy: function FullZoom_destroy() {
// Bug 691614 - zooming support for electrolysis
if (gMultiProcessBrowser)
return;
gPrefService.removeObserver("browser.zoom.", this);
this._cps2.removeObserverForName(this.name, this);
window.removeEventListener("DOMMouseScroll", this, false);
@ -210,6 +218,10 @@ var FullZoom = {
* (optional) browser object displaying the document
*/
onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
// Bug 691614 - zooming support for electrolysis
if (gMultiProcessBrowser)
return;
if (!aURI || (aIsTabSwitch && !this.siteSpecific)) {
this._notifyOnLocationChange();
return;

View File

@ -25,6 +25,10 @@ let gGestureSupport = {
* True to add/init listeners and false to remove/uninit
*/
init: function GS_init(aAddListener) {
// Bug 863514 - Make gesture support work in electrolysis
if (gMultiProcessBrowser)
return;
const gestureEvents = ["SwipeGestureStart",
"SwipeGestureUpdate", "SwipeGestureEnd", "SwipeGesture",
"MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
@ -501,6 +505,10 @@ let gGestureSupport = {
* image
*/
restoreRotationState: function() {
// Bug 863514 - Make gesture support work in electrolysis
if (gMultiProcessBrowser)
return;
if (!(content.document instanceof ImageDocument))
return;

View File

@ -465,23 +465,7 @@ var gPluginHandler = {
}
if (overlay) {
overlay.addEventListener("click", function(aEvent) {
// Have to check that the target is not the link to update the plugin
if (!(aEvent.originalTarget instanceof HTMLAnchorElement) &&
(aEvent.originalTarget.getAttribute('anonid') != 'closeIcon') &&
aEvent.button == 0 && aEvent.isTrusted) {
if (objLoadingContent.pluginFallbackType ==
Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE ||
objLoadingContent.pluginFallbackType ==
Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE)
gPluginHandler._showClickToPlayNotification(browser, true);
else
gPluginHandler.activateSinglePlugin(aEvent.target.ownerDocument.defaultView.top, aPlugin);
aEvent.stopPropagation();
aEvent.preventDefault();
}
}, true);
overlay.addEventListener("click", gPluginHandler._overlayClickListener, true);
let closeIcon = doc.getAnonymousElementByAttribute(aPlugin, "anonid", "closeIcon");
closeIcon.addEventListener("click", function(aEvent) {
if (aEvent.button == 0 && aEvent.isTrusted)
@ -492,6 +476,40 @@ var gPluginHandler = {
gPluginHandler._showClickToPlayNotification(browser);
},
_overlayClickListener: {
handleEvent: function PH_handleOverlayClick(aEvent) {
let plugin = document.getBindingParent(aEvent.target);
let contentWindow = plugin.ownerDocument.defaultView.top;
// gBrowser.getBrowserForDocument does not exist in the case where we
// drag-and-dropped a tab from a window containing only that tab. In
// that case, the window gets destroyed.
let browser = gBrowser.getBrowserForDocument ?
gBrowser.getBrowserForDocument(contentWindow.document) :
null;
// If browser is null here, we've been drag-and-dropped from another
// window, and this is the wrong click handler.
if (!browser) {
aEvent.target.removeEventListener("click", gPluginHandler._overlayClickListener, true);
return;
}
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
// Have to check that the target is not the link to update the plugin
if (!(aEvent.originalTarget instanceof HTMLAnchorElement) &&
(aEvent.originalTarget.getAttribute('anonid') != 'closeIcon') &&
aEvent.button == 0 && aEvent.isTrusted) {
if (objLoadingContent.pluginFallbackType ==
Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE ||
objLoadingContent.pluginFallbackType ==
Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE)
gPluginHandler._showClickToPlayNotification(browser, true);
else
gPluginHandler.activateSinglePlugin(contentWindow, plugin);
aEvent.stopPropagation();
aEvent.preventDefault();
}
}
},
_handlePlayPreviewEvent: function PH_handlePlayPreviewEvent(aPlugin) {
let doc = aPlugin.ownerDocument;
let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
@ -546,8 +564,23 @@ var gPluginHandler = {
reshowClickToPlayNotification: function PH_reshowClickToPlayNotification() {
let browser = gBrowser.selectedBrowser;
if (gPluginHandler._pluginNeedsActivationExceptThese([]))
gPluginHandler._showClickToPlayNotification(browser);
if (!browser._clickToPlayPluginsActivated)
browser._clickToPlayPluginsActivated = new Map();
if (!browser._pluginScriptedState)
browser._pluginScriptedState = gPluginHandler.PLUGIN_SCRIPTED_STATE_NONE;
let contentWindow = browser.contentWindow;
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let doc = contentWindow.document;
let plugins = cwu.plugins;
for (let plugin of plugins) {
let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
if (overlay)
overlay.removeEventListener("click", gPluginHandler._overlayClickListener, true);
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
if (gPluginHandler.canActivatePlugin(objLoadingContent))
gPluginHandler._handleClickToPlayEvent(plugin);
}
},
// returns true if there is a plugin on this page that needs activation

View File

@ -31,6 +31,10 @@ let gBrowserThumbnails = {
_tabEvents: ["TabClose", "TabSelect"],
init: function Thumbnails_init() {
// Bug 863512 - Make page thumbnails work in electrolysis
if (gMultiProcessBrowser)
return;
try {
if (Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled"))
return;
@ -51,6 +55,10 @@ let gBrowserThumbnails = {
},
uninit: function Thumbnails_uninit() {
// Bug 863512 - Make page thumbnails work in electrolysis
if (gMultiProcessBrowser)
return;
PageThumbs.removeExpirationFilter(this);
gBrowser.removeTabsProgressListener(this);
Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);

View File

@ -18,6 +18,7 @@ var gProxyFavIcon = null;
var gLastValidURLStr = "";
var gInPrintPreviewMode = false;
var gContextMenu = null; // nsContextMenu instance
var gMultiProcessBrowser = false;
#ifndef XP_MACOSX
var gEditUIVisible = true;
@ -739,6 +740,8 @@ var gBrowserInit = {
if ("arguments" in window && window.arguments[0])
var uriToLoad = window.arguments[0];
gMultiProcessBrowser = gPrefService.getBoolPref("browser.tabs.remote");
var mustLoadSidebar = false;
Cc["@mozilla.org/eventlistenerservice;1"]
@ -801,18 +804,18 @@ var gBrowserInit = {
// enable global history
try {
gBrowser.docShell.QueryInterface(Ci.nsIDocShellHistory).useGlobalHistory = true;
if (!gMultiProcessBrowser)
gBrowser.docShell.QueryInterface(Ci.nsIDocShellHistory).useGlobalHistory = true;
} catch(ex) {
Cu.reportError("Places database may be locked: " + ex);
}
#ifdef MOZ_E10S_COMPAT
// Bug 666801 - WebProgress support for e10s
#else
// hook up UI through progress listener
gBrowser.addProgressListener(window.XULBrowserWindow);
gBrowser.addTabsProgressListener(window.TabsProgressListener);
#endif
if (!gMultiProcessBrowser) {
// hook up UI through progress listener
gBrowser.addProgressListener(window.XULBrowserWindow);
gBrowser.addTabsProgressListener(window.TabsProgressListener);
}
// setup our common DOMLinkAdded listener
gBrowser.addEventListener("DOMLinkAdded", DOMLinkHandler, false);
@ -975,7 +978,7 @@ var gBrowserInit = {
gBrowser.addEventListener("pageshow", function(event) {
// Filter out events that are not about the document load we are interested in
if (content && event.target == content.document)
setTimeout(pageShowEventHandlers, 0, event);
setTimeout(pageShowEventHandlers, 0, event.persisted);
}, true);
if (uriToLoad && uriToLoad != "about:blank") {
@ -1084,13 +1087,12 @@ var gBrowserInit = {
// apply full zoom settings to tabs restored by the session restore service.
FullZoom.init();
#ifdef MOZ_E10S_COMPAT
// Bug 666804 - NetworkPrioritizer support for e10s
#else
let NP = {};
Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
NP.trackBrowserWindow(window);
#endif
if (!gMultiProcessBrowser) {
let NP = {};
Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
NP.trackBrowserWindow(window);
}
// initialize the session-restore service (in case it's not already running)
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
@ -1134,12 +1136,11 @@ var gBrowserInit = {
gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
#ifdef MOZ_E10S_COMPAT
// Bug 666808 - AeroPeek support for e10s
#else
if (Win7Features)
Win7Features.onOpenWindow();
#endif
if (!gMultiProcessBrowser) {
if (Win7Features)
Win7Features.onOpenWindow();
}
// called when we go into full screen, even if initiated by a web page script
window.addEventListener("fullscreen", onFullScreen, true);
@ -2132,8 +2133,9 @@ function URLBarSetURI(aURI) {
// Replace initial page URIs with an empty string
// only if there's no opener (bug 370555).
// Bug 863515 - Make content.opener checks work in electrolysis.
if (gInitialPages.indexOf(uri.spec) != -1)
value = content.opener ? uri.spec : "";
value = !gMultiProcessBrowser && content.opener ? uri.spec : "";
else
value = losslessDecodeURI(uri);
@ -3625,15 +3627,15 @@ var XULBrowserWindow = {
init: function () {
this.throbberElement = document.getElementById("navigator-throbber");
#ifdef MOZ_E10S_COMPAT
// Bug 666809 - SecurityUI support for e10s
#else
if (gMultiProcessBrowser)
return;
// Initialize the security button's state and tooltip text. Remember to reset
// _hostChanged, otherwise onSecurityChange will short circuit.
var securityUI = gBrowser.securityUI;
this._hostChanged = true;
this.onSecurityChange(null, null, securityUI.state);
#endif
},
destroy: function () {
@ -3800,7 +3802,7 @@ var XULBrowserWindow = {
this.setDefaultStatus(msg);
// Disable menu entries for images, enable otherwise
if (content.document && mimeTypeIsTextBased(content.document.contentType))
if (!gMultiProcessBrowser && content.document && mimeTypeIsTextBased(content.document.contentType))
this.isImage.removeAttribute('disabled');
else
this.isImage.setAttribute('disabled', 'true');
@ -3850,7 +3852,7 @@ var XULBrowserWindow = {
}
// Disable menu entries for images, enable otherwise
if (content.document && mimeTypeIsTextBased(content.document.contentType))
if (!gMultiProcessBrowser && content.document && mimeTypeIsTextBased(content.document.contentType))
this.isImage.removeAttribute('disabled');
else
this.isImage.setAttribute('disabled', 'true');
@ -3866,7 +3868,7 @@ var XULBrowserWindow = {
var browser = gBrowser.selectedBrowser;
if (aWebProgress.DOMWindow == content) {
if ((location == "about:blank" && !content.opener) ||
if ((location == "about:blank" && (gMultiProcessBrowser || !content.opener)) ||
location == "") { // Second condition is for new tabs, otherwise
// reload function is enabled until tab is refreshed.
this.reloadCommand.setAttribute("disabled", "true");
@ -3920,7 +3922,7 @@ var XULBrowserWindow = {
}
// Disable find commands in documents that ask for them to be disabled.
if (aLocationURI &&
if (!gMultiProcessBrowser && aLocationURI &&
(aLocationURI.schemeIs("about") || aLocationURI.schemeIs("chrome"))) {
// Don't need to re-enable/disable find commands for same-document location changes
// (e.g. the replaceStates in about:addons)
@ -4033,6 +4035,9 @@ var XULBrowserWindow = {
gURLBar.removeAttribute("level");
}
if (gMultiProcessBrowser)
return;
// Don't pass in the actual location object, since it can cause us to
// hold on to the window object too long. Just pass in the fields we
// care about. (bug 424829)
@ -4253,8 +4258,9 @@ var TabsProgressListener = {
// We can't look for this during onLocationChange since at that point the
// document URI is not yet the about:-uri of the error page.
let doc = aWebProgress.DOMWindow.document;
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
let doc = gMultiProcessBrowser ? null : aWebProgress.DOMWindow.document;
if (!gMultiProcessBrowser &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
Components.isSuccessCode(aStatus) &&
doc.documentURI.startsWith("about:") &&
!doc.documentURI.toLowerCase().startsWith("about:blank") &&
@ -6133,7 +6139,12 @@ function AddKeywordForSearchField() {
}
function SwitchDocumentDirection(aWindow) {
aWindow.document.dir = (aWindow.document.dir == "ltr" ? "rtl" : "ltr");
// document.dir can also be "auto", in which case it won't change
if (aWindow.document.dir == "ltr" || aWindow.document.dir == "") {
aWindow.document.dir = "rtl";
} else if (aWindow.document.dir == "rtl") {
aWindow.document.dir = "ltr";
}
for (var run = 0; run < aWindow.frames.length; run++)
SwitchDocumentDirection(aWindow.frames[run]);
}
@ -6207,7 +6218,8 @@ function isTabEmpty(aTab) {
if (!isBlankPageURL(browser.currentURI.spec))
return false;
if (browser.contentWindow.opener)
// Bug 863515 - Make content.opener checks work in electrolysis.
if (!gMultiProcessBrowser && browser.contentWindow.opener)
return false;
if (browser.sessionHistory && browser.sessionHistory.count >= 2)
@ -6754,8 +6766,10 @@ let gPrivateBrowsingUI = {
}
}
if (gURLBar) {
// Disable switch to tab autocompletion for private windows
if (gURLBar &&
!PrivateBrowsingUtils.permanentPrivateBrowsing) {
// Disable switch to tab autocompletion for private windows
// (not for "Always use private browsing" mode)
gURLBar.setAttribute("autocompletesearchparam", "");
}
}
@ -6778,9 +6792,10 @@ function switchToTabHavingURI(aURI, aOpenNew) {
// This will switch to the tab in aWindow having aURI, if present.
function switchIfURIInWindow(aWindow) {
// Only switch to the tab if neither the source and desination window are
// private.
if (PrivateBrowsingUtils.isWindowPrivate(window) ||
PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
// private and they are not in permanent private borwsing mode
if ((PrivateBrowsingUtils.isWindowPrivate(window) ||
PrivateBrowsingUtils.isWindowPrivate(aWindow)) &&
!PrivateBrowsingUtils.permanentPrivateBrowsing) {
return false;
}

View File

@ -452,6 +452,9 @@
if (this.mBlank)
return false;
if (gMultiProcessBrowser)
return true;
// Don't show progress indicators in tabs for about: URIs
// pointing to local resources.
try {
@ -529,8 +532,10 @@
if (this._shouldShowProgress(aRequest)) {
if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
this.mTab.setAttribute("busy", "true");
if (!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
this.mTabBrowser.setTabTitleLoading(this.mTab);
if (!gMultiProcessBrowser) {
if (!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
this.mTabBrowser.setTabTitleLoading(this.mTab);
}
}
if (this.mTab.selected)
@ -631,9 +636,11 @@
// Don't clear the favicon if this onLocationChange was
// triggered by a pushState or a replaceState. See bug 550565.
if (aWebProgress.isLoadingDocument &&
!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE))
this.mBrowser.mIconURL = null;
if (!gMultiProcessBrowser) {
if (aWebProgress.isLoadingDocument &&
!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE))
this.mBrowser.mIconURL = null;
}
let autocomplete = this.mTabBrowser._placesAutocomplete;
if (this.mBrowser.registeredOpenURI) {
@ -643,7 +650,8 @@
// Tabs in private windows aren't registered as "Open" so
// that they don't appear as switch-to-tab candidates.
if (!isBlankPageURL(aLocation.spec) &&
!PrivateBrowsingUtils.isWindowPrivate(window)) {
(!PrivateBrowsingUtils.isWindowPrivate(window) ||
PrivateBrowsingUtils.permanentPrivateBrowsing)) {
autocomplete.registerOpenPage(aLocation);
this.mBrowser.registeredOpenURI = aLocation;
}
@ -755,6 +763,10 @@
<parameter name="aTab"/>
<body>
<![CDATA[
// Bug 691610 - e10s support for useDefaultIcon
if (gMultiProcessBrowser)
return;
var browser = this.getBrowserForTab(aTab);
var docURIObject = browser.contentDocument.documentURIObject;
var icon = null;
@ -916,22 +928,21 @@
// Update the URL bar.
var loc = this.mCurrentBrowser.currentURI;
#ifdef MOZ_E10S_COMPAT
// Bug 666801 - WebProgress support for e10s and
// Bug 666809 - SecurityUI support for e10s
#else
var webProgress = this.mCurrentBrowser.webProgress;
var securityUI = this.mCurrentBrowser.securityUI;
if (!gMultiProcessBrowser) {
var webProgress = this.mCurrentBrowser.webProgress;
var securityUI = this.mCurrentBrowser.securityUI;
this._callProgressListeners(null, "onLocationChange",
[webProgress, null, loc, 0], true,
false);
this._callProgressListeners(null, "onLocationChange",
[webProgress, null, loc, 0], true,
false);
if (securityUI) {
this._callProgressListeners(null, "onSecurityChange",
[webProgress, null, securityUI.state], true, false);
if (securityUI) {
this._callProgressListeners(null, "onSecurityChange",
[webProgress, null, securityUI.state], true, false);
}
}
#endif
var listener = this.mTabListeners[this.tabContainer.selectedIndex] || null;
if (listener && listener.mStateFlags) {
@ -945,11 +956,9 @@
this.mCurrentTab.removeAttribute("unread");
this.selectedTab.lastAccessed = Date.now();
#ifdef MOZ_E10S_COMPAT
// Bug 666816 - TypeAheadFind support for e10s
#else
this._fastFind.setDocShell(this.mCurrentBrowser.docShell);
#endif
if (!gMultiProcessBrowser)
this._fastFind.setDocShell(this.mCurrentBrowser.docShell);
this.updateTitlebar();
@ -1038,15 +1047,18 @@
// Otherwise, focus the content area.
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
let newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {});
// for anchors, use FLAG_SHOWRING so that it is clear what link was
// last clicked when switching back to that tab
let focusFlags = fm.FLAG_NOSCROLL;
if (newFocusedElement &&
(newFocusedElement instanceof HTMLAnchorElement ||
newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
focusFlags |= fm.FLAG_SHOWRING;
if (!gMultiProcessBrowser) {
let newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {});
// for anchors, use FLAG_SHOWRING so that it is clear what link was
// last clicked when switching back to that tab
if (newFocusedElement &&
(newFocusedElement instanceof HTMLAnchorElement ||
newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
focusFlags |= fm.FLAG_SHOWRING;
}
fm.setFocus(newBrowser, focusFlags);
} while (false);
}
@ -1105,12 +1117,14 @@
// At this point, we now have a URI.
// Let's try to unescape it using a character set
// in case the URI is not ASCII.
try {
var characterSet = browser.contentDocument.characterSet;
const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
.getService(Components.interfaces.nsITextToSubURI);
title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
} catch(ex) { /* Do nothing. */ }
if (!gMultiProcessBrowser) {
try {
var characterSet = browser.contentDocument.characterSet;
const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
.getService(Components.interfaces.nsITextToSubURI);
title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
} catch(ex) { /* Do nothing. */ }
}
crop = "center";
@ -1386,11 +1400,9 @@
const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
.createInstance(Components.interfaces.nsIWebProgress);
filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
#ifdef MOZ_E10S_COMPAT
// Bug 666801 - WebProgress support for e10s
#else
b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
#endif
if (!gMultiProcessBrowser)
b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
this.mTabListeners[position] = tabListener;
this.mTabFilters[position] = filter;
@ -1685,18 +1697,20 @@
evt.initUIEvent("TabClose", true, false, window, aTabWillBeMoved ? 1 : 0);
aTab.dispatchEvent(evt);
// Prevent this tab from showing further dialogs, since we're closing it
var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils);
windowUtils.preventFurtherDialogs();
if (!gMultiProcessBrowser) {
// Prevent this tab from showing further dialogs, since we're closing it
var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils);
windowUtils.preventFurtherDialogs();
}
// Remove the tab's filter and progress listener.
const filter = this.mTabFilters[aTab._tPos];
#ifdef MOZ_E10S_COMPAT
// Bug 666801 - WebProgress support for e10s
#else
browser.webProgress.removeProgressListener(filter);
#endif
if (!gMultiProcessBrowser)
browser.webProgress.removeProgressListener(filter);
filter.removeProgressListener(this.mTabListeners[aTab._tPos]);
this.mTabListeners[aTab._tPos].destroy();

View File

@ -51,6 +51,117 @@ function part3() {
function part4() {
ok(PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should have a click-to-play notification in the initial tab again");
gBrowser.removeCurrentTab();
gBrowser.selectedBrowser.addEventListener("PluginBindingAttached", handleEvent, true, true);
gNextTest = part5;
gBrowser.selectedBrowser.contentDocument.location = gHttpTestRoot + "plugin_test.html";
}
function part5() {
gBrowser.selectedBrowser.removeEventListener("PluginBindingAttached", handleEvent);
ok(PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should have a click-to-play notification in the initial tab");
gNextTest = part6;
gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
gNewWindow.addEventListener("load", handleEvent, true);
}
function part6() {
gNewWindow.removeEventListener("load", handleEvent);
let condition = function() PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser);
waitForCondition(condition, part7, "Waited too long for click-to-play notification");
}
function part7() {
ok(PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser), "Should have a click-to-play notification in the tab in the new window");
ok(!PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should not have a click-to-play notification in the old window now");
let plugin = gNewWindow.gBrowser.selectedBrowser.contentDocument.getElementById("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "plugin should not be activated");
EventUtils.synthesizeMouseAtCenter(plugin, {}, gNewWindow.gBrowser.selectedBrowser.contentWindow);
let condition = function() objLoadingContent.activated;
waitForCondition(condition, part8, "waited too long for plugin to activate");
}
function part8() {
ok(!PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser), "Should not have a click-to-play notification in the tab in the new window now");
let plugin = gNewWindow.gBrowser.selectedBrowser.contentDocument.getElementById("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "plugin should be activated now");
gNewWindow.close();
gBrowser.selectedTab = gBrowser.addTab();
gNextTest = part9;
gBrowser.selectedBrowser.addEventListener("PluginBindingAttached", handleEvent, true, true);
// This test page contains an "invisible" plugin. It doesn't script it,
// but when we do later in this test, it will trigger the popup notification.
gBrowser.selectedBrowser.contentDocument.location = gHttpTestRoot + "plugin_test_noScriptNoPopup.html";
}
function part9() {
gBrowser.selectedBrowser.removeEventListener("PluginBindingAttached", handleEvent);
ok(PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should have a click-to-play notification in the initial tab");
gNextTest = part10;
gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
gNewWindow.addEventListener("load", handleEvent, true);
}
function part10() {
gNewWindow.removeEventListener("load", handleEvent);
let condition = function() PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser);
waitForCondition(condition, part11, "Waited too long for click-to-play notification");
}
function part11() {
ok(!PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should not have a click-to-play notification in the old window now");
let notification = PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser);
ok(notification, "Should have a click-to-play notification in the tab in the new window");
// we have to actually show the panel to get the bindings to instantiate
notification.options.eventCallback = part12;
// this scripts the plugin, triggering the popup notification
try {
gNewWindow.gBrowser.selectedBrowser.contentDocument.getElementById("test").wrappedJSObject.getObjectValue();
} catch (e) {}
}
function part12() {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser);
notification.options.eventCallback = null;
let centerAction = null;
for (let action of notification.options.centerActions) {
if (action.message == "Test") {
centerAction = action;
break;
}
}
ok(centerAction, "Found center action for the Test plugin");
let centerItem = null;
for (let item of centerAction.popupnotification.childNodes) {
if (item.action == centerAction) {
centerItem = item;
break;
}
}
ok(centerItem, "Found center item for the Test plugin");
// "click" the button to activate the Test plugin
centerItem.runCallback.apply(centerItem);
let plugin = gNewWindow.gBrowser.selectedBrowser.contentDocument.getElementById("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
let condition = function() objLoadingContent.activated;
waitForCondition(condition, part13, "Waited too long for plugin to activate via center action");
}
function part13() {
ok(!PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser), "Should not have a click-to-play notification in the tab in the new window");
let plugin = gNewWindow.gBrowser.selectedBrowser.contentDocument.getElementById("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "Plugin should be activated via center action");
gNewWindow.close();
finish();
}

View File

@ -319,6 +319,10 @@ let SessionStoreInternal = {
// Whether session has been initialized
_sessionInitialized: false,
// True if session store is disabled by multi-process browsing.
// See bug 516755.
_disabledForMultiProcess: false,
// The original "sessionstore.resume_session_once" preference value before it
// was modified by saveState. saveState will set the
// "sessionstore.resume_session_once" to true when the
@ -367,6 +371,8 @@ let SessionStoreInternal = {
// Do pref migration before we store any values and start observing changes
this._migratePrefs();
this._disabledForMultiProcess = this._prefBranch.getBoolPref("tabs.remote");
// this pref is only read at startup, so no need to observe it
this._sessionhistory_max_entries =
this._prefBranch.getIntPref("sessionhistory.max_entries");
@ -591,6 +597,9 @@ let SessionStoreInternal = {
* Handle notifications
*/
observe: function ssi_observe(aSubject, aTopic, aData) {
if (this._disabledForMultiProcess)
return;
switch (aTopic) {
case "domwindowopened": // catch new windows
this.onOpen(aSubject);
@ -654,6 +663,9 @@ let SessionStoreInternal = {
* Implement nsIDOMEventListener for handling various window and tab events
*/
handleEvent: function ssi_handleEvent(aEvent) {
if (this._disabledForMultiProcess)
return;
var win = aEvent.currentTarget.ownerDocument.defaultView;
switch (aEvent.type) {
case "load":

View File

@ -7308,12 +7308,10 @@ exports.Output = Output;
* Functions and data related to the execution of a command
*/
exports.createExecutionContext = function(requisition) {
return {
var context = {
exec: requisition.exec.bind(requisition),
update: requisition.update.bind(requisition),
updateExec: requisition.updateExec.bind(requisition),
document: requisition.document,
environment: requisition.environment,
createView: view.createView,
typedData: function(data, type) {
return {
@ -7334,6 +7332,18 @@ exports.createExecutionContext = function(requisition) {
return Promise.defer();
}
};
Object.defineProperty(context, 'environment', {
get: function() { return requisition.environment; },
enumerable : true
});
Object.defineProperty(context, 'document', {
get: function() { return requisition.document; },
enumerable : true
});
return context;
};

View File

@ -653,12 +653,12 @@ function testKeyboardAccessibility(callback) {
"The 0 item should be focused now.");
EventUtils.sendKey("END", gDebugger);
is(gVariablesView.getFocusedItem().name, "foo",
"The foo item should be focused now.");
is(gVariablesView.getFocusedItem().name, "bar",
"The bar item should be focused now.");
EventUtils.sendKey("DOWN", gDebugger);
is(gVariablesView.getFocusedItem().name, "bar",
"The bar item should be focused now.");
"The bar item should still be focused now.");
EventUtils.sendKey("UP", gDebugger);
is(gVariablesView.getFocusedItem().name, "foo",
@ -669,10 +669,14 @@ function testKeyboardAccessibility(callback) {
"The foo item should still be focused now.");
EventUtils.sendKey("PAGE_DOWN", gDebugger);
is(gVariablesView.getFocusedItem().name, "foo",
"The foo item should still be focused now.");
is(gVariablesView.getFocusedItem().name, "bar",
"The bar item should be focused now.");
EventUtils.sendKey("PAGE_UP", gDebugger);
is(gVariablesView.getFocusedItem().name, "someProp7",
"The someProp7 item should be focused now.");
EventUtils.sendKey("UP", gDebugger);
is(gVariablesView.getFocusedItem().name, "__proto__",
"The __proto__ item should be focused now.");
@ -684,10 +688,6 @@ function testKeyboardAccessibility(callback) {
is(gVariablesView.getFocusedItem().name, "get",
"The get item should be focused now.");
EventUtils.sendKey("UP", gDebugger);
is(gVariablesView.getFocusedItem().name, "p8",
"The p8 item should be focused now.");
EventUtils.sendKey("HOME", gDebugger);
is(gVariablesView.getFocusedItem().name, "someProp0",
"The someProp0 item should be focused now.");
@ -828,6 +828,18 @@ function testKeyboardAccessibility(callback) {
is(gVariablesView.getFocusedItem().expanded, false,
"The top-level __proto__ item should not be expanded.");
EventUtils.sendKey("END", gDebugger);
is(gVariablesView.getFocusedItem().name, "foo",
"The foo scope should be focused.");
EventUtils.sendKey("PAGE_UP", gDebugger);
is(gVariablesView.getFocusedItem().name, "__proto__",
"The __proto__ property should be focused.");
EventUtils.sendKey("PAGE_DOWN", gDebugger);
is(gVariablesView.getFocusedItem().name, "foo",
"The foo scope should be focused.");
executeSoon(callback);
});
});

View File

@ -275,25 +275,31 @@ TabTarget.prototype = {
this._setupRemoteListeners();
if (this.isRemote) {
// In the remote debugging case, the protocol connection will have been
// already initialized in the connection screen code.
this._remote.resolve(null);
} else {
let attachTab = () => {
this._client.attachTab(this._form.actor, (aResponse, aTabClient) => {
if (!aTabClient) {
this._remote.reject("Unable to attach to the tab");
return;
}
this.threadActor = aResponse.threadActor;
this._remote.resolve(null);
});
};
if (this.isLocalTab) {
this._client.connect((aType, aTraits) => {
this._client.listTabs(aResponse => {
this._form = aResponse.tabs[aResponse.selected];
this._client.attachTab(this._form.actor, (aResponse, aTabClient) => {
if (!aTabClient) {
this._remote.reject("Unable to attach to the tab");
return;
}
this.threadActor = aResponse.threadActor;
this._remote.resolve(null);
});
attachTab();
});
});
} else if (!this.chrome) {
// In the remote debugging case, the protocol connection will have been
// already initialized in the connection screen code.
attachTab();
} else {
// Remote chrome debugging doesn't need anything at this point.
this._remote.resolve(null);
}
return this._remote.promise;

View File

@ -515,7 +515,8 @@ let L10N = new ViewHelpers.L10N(NET_STRINGS_URI);
* Shortcuts for accessing various network monitor preferences.
*/
let Prefs = new ViewHelpers.Prefs("devtools.netmonitor", {
networkDetailsWidth: ["Int", "panes-network-details-width"]
networkDetailsWidth: ["Int", "panes-network-details-width"],
networkDetailsHeight: ["Int", "panes-network-details-height"]
});
/**

View File

@ -7,6 +7,7 @@
const EPSILON = 0.001;
const REQUESTS_REFRESH_RATE = 50; // ms
const REQUESTS_HEADERS_SAFE_BOUNDS = 30; // px
const REQUESTS_WATERFALL_SAFE_BOUNDS = 100; // px
const REQUESTS_WATERFALL_BACKGROUND_PATTERN = [5, 250, 1000, 2000]; // ms
const DEFAULT_HTTP_VERSION = "HTTP/1.1";
@ -102,6 +103,7 @@ let NetMonitorView = {
this._expandPaneString = L10N.getStr("expandDetailsPane");
this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth);
this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight);
this.toggleDetailsPane({ visible: false });
},
@ -112,6 +114,7 @@ let NetMonitorView = {
dumpn("Destroying the NetMonitorView panes");
Prefs.networkDetailsWidth = this._detailsPane.getAttribute("width");
Prefs.networkDetailsHeight = this._detailsPane.getAttribute("height");
this._detailsPane = null;
this._detailsPaneToggleButton = null;
@ -592,6 +595,27 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
// the window is resized, this needs to be invalidated.
if (aReset) {
this._cachedWaterfallWidth = 0;
let table = $("#network-table");
let toolbar = $("#requests-menu-toolbar");
let columns = [
[".requests-menu-waterfall", "waterfall-overflows"],
[".requests-menu-size", "size-overflows"],
[".requests-menu-type", "type-overflows"],
[".requests-menu-domain", "domain-overflows"]
];
// Flush headers.
columns.forEach(([, attribute]) => table.removeAttribute(attribute));
let availableWidth = toolbar.getBoundingClientRect().width;
// Hide overflowing columns.
columns.forEach(([className, attribute]) => {
let bounds = $(".requests-menu-header" + className).getBoundingClientRect();
if (bounds.right > availableWidth - REQUESTS_HEADERS_SAFE_BOUNDS) {
table.setAttribute(attribute, "");
}
});
}
// Determine the scaling to be applied to all the waterfalls so that

View File

@ -13,8 +13,36 @@
/* Responsive sidebar */
@media (max-width: 700px) {
#toolbar-spacer,
#details-pane-toggle,
#details-pane[pane-collapsed],
.requests-menu-waterfall {
display: none;
}
}
@media (min-width: 701px) {
#network-table[waterfall-overflows] .requests-menu-waterfall {
display: none;
}
#network-table[size-overflows] .requests-menu-size {
display: none;
}
#network-table[type-overflows] .requests-menu-type {
display: none;
}
#network-table[domain-overflows] .requests-menu-domain {
display: none;
}
#network-table[type-overflows] .requests-menu-domain {
-moz-box-flex: 1;
}
#network-table[domain-overflows] .requests-menu-file {
-moz-box-flex: 1;
}
}

View File

@ -46,7 +46,7 @@
class="plain requests-menu-header requests-menu-waterfall"
value="&netmonitorUI.toolbar.waterfall;"
crop="end"/>
<spacer flex="1"/>
<spacer id="toolbar-spacer" flex="1"/>
<toolbarbutton id="details-pane-toggle"
class="devtools-toolbarbutton"
tooltiptext="&netmonitorUI.panesButton.tooltip;"

View File

@ -25,7 +25,7 @@ function test() {
statusText: "OK",
type: "json",
fullMimeType: "text/json; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 83.95),
size: L10N.getFormatStr("networkMenu.sizeKB", L10N.numberWithDecimals(85975/1024, 2)),
time: true
});

View File

@ -9,13 +9,20 @@ function test() {
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
// This test reopens the network monitor a bunch of times, for different
// hosts (bottom, side, window). This seems to be slow on debug builds.
requestLongerTimeout(2);
let prefsToCheck = {
networkDetailsWidth: {
newValue: ~~(Math.random() * 200 + 100),
validate: () =>
~~aMonitor._view._detailsPane.getAttribute("width"),
modifyFrontend: (aValue) =>
aMonitor._view._detailsPane.setAttribute("width", aValue)
validate: ($) => ~~$("#details-pane").getAttribute("width"),
modifyFrontend: ($, aValue) => $("#details-pane").setAttribute("width", aValue)
},
networkDetailsHeight: {
newValue: ~~(Math.random() * 300 + 100),
validate: ($) => ~~$("#details-pane").getAttribute("height"),
modifyFrontend: ($, aValue) => $("#details-pane").setAttribute("height", aValue)
},
/* add more prefs here... */
};
@ -39,7 +46,7 @@ function test() {
is(currentValue, firstValue,
"Pref " + name + " should be equal to first value: " + firstValue);
is(currentValue, validate(),
is(currentValue, validate(aMonitor.panelWin.$),
"Pref " + name + " should validate: " + currentValue);
}
}
@ -54,14 +61,14 @@ function test() {
let validate = prefsToCheck[name].validate;
let modifyFrontend = prefsToCheck[name].modifyFrontend;
modifyFrontend(newValue);
modifyFrontend(aMonitor.panelWin.$, newValue);
info("Modified UI element affecting " + name + " to: " + newValue);
is(currentValue, firstValue,
"Pref " + name + " should still be equal to first value: " + firstValue);
isnot(currentValue, newValue,
"Pref " + name + " should't yet be equal to second value: " + newValue);
is(newValue, validate(),
is(newValue, validate(aMonitor.panelWin.$),
"The UI element affecting " + name + " should validate: " + newValue);
}
}
@ -74,13 +81,12 @@ function test() {
let firstValue = prefsToCheck[name].firstValue;
let newValue = prefsToCheck[name].newValue;
let validate = prefsToCheck[name].validate;
let modifyFrontend = prefsToCheck[name].modifyFrontend;
isnot(currentValue, firstValue,
"Pref " + name + " should't be equal to first value: " + firstValue);
is(currentValue, newValue,
"Pref " + name + " should now be equal to second value: " + newValue);
is(newValue, validate(),
is(newValue, validate(aMonitor.panelWin.$),
"The UI element affecting " + name + " should validate: " + newValue);
}
}
@ -95,36 +101,117 @@ function test() {
let validate = prefsToCheck[name].validate;
let modifyFrontend = prefsToCheck[name].modifyFrontend;
modifyFrontend(firstValue);
modifyFrontend(aMonitor.panelWin.$, firstValue);
info("Modified UI element affecting " + name + " to: " + firstValue);
isnot(currentValue, firstValue,
"Pref " + name + " should't yet be equal to first value: " + firstValue);
is(currentValue, newValue,
"Pref " + name + " should still be equal to second value: " + newValue);
is(firstValue, validate(),
is(firstValue, validate(aMonitor.panelWin.$),
"The UI element affecting " + name + " should validate: " + firstValue);
}
}
storeFirstPrefValues();
function testBottom() {
info("Testing prefs reload for a bottom host.");
storeFirstPrefValues();
// Validate and modify.
validateFirstPrefValues();
modifyFrontend();
restartNetMonitor(aMonitor).then(([,, aNewMonitor]) => {
aMonitor = aNewMonitor;
// Validate and modify while toolbox is on the bottom.
validateFirstPrefValues();
modifyFrontend();
// Revalidate and reset.
validateNewPrefValues();
resetFrontend();
restartNetMonitor(aMonitor).then(([,, aNewMonitor]) => {
aMonitor = aNewMonitor;
return restartNetMonitor(aMonitor)
.then(([,, aNewMonitor]) => {
aMonitor = aNewMonitor;
// Revalidate and finish.
validateFirstPrefValues();
teardown(aMonitor).then(finish);
});
});
// Revalidate and reset frontend while toolbox is on the bottom.
validateNewPrefValues();
resetFrontend();
return restartNetMonitor(aMonitor);
})
.then(([,, aNewMonitor]) => {
aMonitor = aNewMonitor;
// Revalidate.
validateFirstPrefValues();
});
}
function testSide() {
info("Moving toolbox to the side...");
return aMonitor._toolbox.switchHost(Toolbox.HostType.SIDE)
.then(() => {
info("Testing prefs reload for a side host.");
storeFirstPrefValues();
// Validate and modify frontend while toolbox is on the side.
validateFirstPrefValues();
modifyFrontend();
return restartNetMonitor(aMonitor);
})
.then(([,, aNewMonitor]) => {
aMonitor = aNewMonitor;
// Revalidate and reset frontend while toolbox is on the side.
validateNewPrefValues();
resetFrontend();
return restartNetMonitor(aMonitor);
})
.then(([,, aNewMonitor]) => {
aMonitor = aNewMonitor;
// Revalidate.
validateFirstPrefValues();
});
}
function testWindow() {
info("Moving toolbox into a window...");
return aMonitor._toolbox.switchHost(Toolbox.HostType.WINDOW)
.then(() => {
info("Testing prefs reload for a window host.");
storeFirstPrefValues();
// Validate and modify frontend while toolbox is in a window.
validateFirstPrefValues();
modifyFrontend();
return restartNetMonitor(aMonitor);
})
.then(([,, aNewMonitor]) => {
aMonitor = aNewMonitor;
// Revalidate and reset frontend while toolbox is in a window.
validateNewPrefValues();
resetFrontend();
return restartNetMonitor(aMonitor);
})
.then(([,, aNewMonitor]) => {
aMonitor = aNewMonitor;
// Revalidate.
validateFirstPrefValues();
});
}
function cleanupAndFinish() {
info("Moving toolbox back to the bottom...");
aMonitor._toolbox.switchHost(Toolbox.HostType.BOTTOM)
.then(() => teardown(aMonitor))
.then(finish);
}
testBottom()
.then(testSide)
.then(testWindow)
.then(cleanupAndFinish);
});
}

View File

@ -73,7 +73,7 @@ function test() {
is(responseScope.querySelector(".name").getAttribute("value"),
L10N.getStr("responseHeaders") + " (" +
L10N.getFormatStr("networkMenu.sizeKB", L10N.numberWithDecimals(0.168, 3)) + ")",
L10N.getFormatStr("networkMenu.sizeKB", L10N.numberWithDecimals(173/1024, 3)) + ")",
"The response headers scope doesn't have the correct title.");
ok(requestScope.querySelector(".name").getAttribute("value").contains(

View File

@ -7,6 +7,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
let { Promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
let { TargetFactory } = Cu.import("resource:///modules/devtools/Target.jsm", {});
let { Toolbox } = Cu.import("resource:///modules/devtools/Toolbox.jsm", {});
let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/netmonitor/test/";

View File

@ -506,7 +506,7 @@ DeveloperToolbar.prototype.destroy = function DT_destroy()
let tabbrowser = this._chromeWindow.getBrowser();
tabbrowser.tabContainer.removeEventListener("TabSelect", this, false);
tabbrowser.tabContainer.removeEventListener("TabClose", this, false);
tabbrowser.removeEventListener("load", this, true);
tabbrowser.removeEventListener("load", this, true);
tabbrowser.removeEventListener("beforeunload", this, true);
Array.prototype.forEach.call(tabbrowser.tabs, this._stopErrorsCount, this);

View File

@ -52,6 +52,7 @@ const STR = Services.strings.createBundle(DBG_STRINGS_URI);
*/
this.VariablesView = function VariablesView(aParentNode, aFlags = {}) {
this._store = new Map();
this._items = [];
this._itemsByElement = new WeakMap();
this._prevHierarchy = new Map();
this._currHierarchy = new Map();
@ -103,6 +104,7 @@ VariablesView.prototype = {
let scope = new Scope(this, aName);
this._store.set(scope.id, scope);
this._items.push(scope);
this._currHierarchy.set(aName, scope);
this._itemsByElement.set(scope._target, scope);
scope.header = !!aName;
@ -135,6 +137,7 @@ VariablesView.prototype = {
}
this._store.clear();
this._items.length = 0;
this._itemsByElement.clear();
this._appendEmptyNotice();
@ -161,6 +164,7 @@ VariablesView.prototype = {
let currList = this._list = this.document.createElement("scrollbox");
this._store.clear();
this._items.length = 0;
this._itemsByElement.clear();
this._emptyTimeout = this.window.setTimeout(function() {
@ -529,64 +533,73 @@ VariablesView.prototype = {
},
/**
* Focuses the first visible variable or property in this container.
* Find the first item in the tree of visible items in this container that
* matches the predicate. Searches in visual order (the order seen by the
* user). Descends into each scope to check the scope and its children.
*
* @param function aPredicate
* A function that returns true when a match is found.
* @return Scope | Variable | Property
* The first visible scope, variable or property, or null if nothing
* is found.
*/
focusFirstVisibleNode: function VV_focusFirstVisibleNode() {
let property, variable, scope;
for (let [, item] of this._currHierarchy) {
if (!item.focusable) {
continue;
}
if (item instanceof Property) {
property = item;
break;
} else if (item instanceof Variable) {
variable = item;
break;
} else if (item instanceof Scope) {
scope = item;
break;
_findInVisibleItems: function VV__findInVisibleItems(aPredicate) {
for (let scope of this._items) {
let result = scope._findInVisibleItems(aPredicate);
if (result) {
return result;
}
}
if (scope) {
this._focusItem(scope);
} else if (variable) {
this._focusItem(variable);
} else if (property) {
this._focusItem(property);
return null;
},
/**
* Find the last item in the tree of visible items in this container that
* matches the predicate. Searches in reverse visual order (opposite of the
* order seen by the user). Descends into each scope to check the scope and
* its children.
*
* @param function aPredicate
* A function that returns true when a match is found.
* @return Scope | Variable | Property
* The last visible scope, variable or property, or null if nothing
* is found.
*/
_findInVisibleItemsReverse: function VV__findInVisibleItemsReverse(aPredicate) {
for (let i = this._items.length - 1; i >= 0; i--) {
let scope = this._items[i];
let result = scope._findInVisibleItemsReverse(aPredicate);
if (result) {
return result;
}
}
return null;
},
/**
* Focuses the first visible scope, variable, or property in this container.
*/
focusFirstVisibleNode: function VV_focusFirstVisibleNode() {
let focusableItem = this._findInVisibleItems(item => item.focusable);
if (focusableItem) {
this._focusItem(focusableItem);
}
this._parent.scrollTop = 0;
this._parent.scrollLeft = 0;
},
/**
* Focuses the last visible variable or property in this container.
* Focuses the last visible scope, variable, or property in this container.
*/
focusLastVisibleNode: function VV_focusLastVisibleNode() {
let property, variable, scope;
let focusableItem = this._findInVisibleItemsReverse(item => item.focusable);
for (let [, item] of this._currHierarchy) {
if (!item.focusable) {
continue;
}
if (item instanceof Property) {
property = item;
} else if (item instanceof Variable) {
variable = item;
} else if (item instanceof Scope) {
scope = item;
}
}
if (property && (!variable || property.isDescendantOf(variable))) {
this._focusItem(property);
} else if (variable && (!scope || variable.isDescendantOf(scope))) {
this._focusItem(variable);
} else if (scope) {
this._focusItem(scope);
this._parent.scrollTop = this._parent.scrollHeight;
this._parent.scrollLeft = 0;
if (focusableItem) {
this._focusItem(focusableItem);
}
this._parent.scrollTop = this._parent.scrollHeight;
this._parent.scrollLeft = 0;
},
/**
@ -888,6 +901,7 @@ VariablesView.prototype = {
_window: null,
_store: null,
_items: null,
_prevHierarchy: null,
_currHierarchy: null,
_enumVisible: true,
@ -1082,6 +1096,8 @@ function Scope(aView, aName, aFlags = {}) {
this.separatorStr = aView.separatorStr;
this._store = new Map();
this._enumItems = [];
this._nonEnumItems = [];
this._init(aName.trim(), aFlags);
}
@ -1785,6 +1801,89 @@ Scope.prototype = {
return null;
},
/**
* Find the first item in the tree of visible items in this item that matches
* the predicate. Searches in visual order (the order seen by the user).
* Tests itself, then descends into first the enumerable children and then
* the non-enumerable children (since they are presented in separate groups).
*
* @param function aPredicate
* A function that returns true when a match is found.
* @return Scope | Variable | Property
* The first visible scope, variable or property, or null if nothing
* is found.
*/
_findInVisibleItems: function S__findInVisibleItems(aPredicate) {
if (aPredicate(this)) {
return this;
}
if (this._isExpanded) {
if (this._variablesView._enumVisible) {
for (let item of this._enumItems) {
let result = item._findInVisibleItems(aPredicate);
if (result) {
return result;
}
}
}
if (this._variablesView._nonEnumVisible) {
for (let item of this._nonEnumItems) {
let result = item._findInVisibleItems(aPredicate);
if (result) {
return result;
}
}
}
}
return null;
},
/**
* Find the last item in the tree of visible items in this item that matches
* the predicate. Searches in reverse visual order (opposite of the order
* seen by the user). Descends into first the non-enumerable children, then
* the enumerable children (since they are presented in separate groups), and
* finally tests itself.
*
* @param function aPredicate
* A function that returns true when a match is found.
* @return Scope | Variable | Property
* The last visible scope, variable or property, or null if nothing
* is found.
*/
_findInVisibleItemsReverse: function S__findInVisibleItemsReverse(aPredicate) {
if (this._isExpanded) {
if (this._variablesView._nonEnumVisible) {
for (let i = this._nonEnumItems.length - 1; i >= 0; i--) {
let item = this._nonEnumItems[i];
let result = item._findInVisibleItemsReverse(aPredicate);
if (result) {
return result;
}
}
}
if (this._variablesView._enumVisible) {
for (let i = this._enumItems.length - 1; i >= 0; i--) {
let item = this._enumItems[i];
let result = item._findInVisibleItemsReverse(aPredicate);
if (result) {
return result;
}
}
}
}
if (aPredicate(this)) {
return this;
}
return null;
},
/**
* Gets top level variables view instance.
* @return VariablesView
@ -1854,7 +1953,9 @@ Scope.prototype = {
_name: null,
_title: null,
_enum: null,
_enumItems: null,
_nonenum: null,
_nonEnumItems: null,
_throbber: null
};
@ -2167,8 +2268,10 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
this._nameString == "this" ||
this._nameString == "<exception>") {
this.ownerView._lazyAppend(aImmediateFlag, true, this._target);
this.ownerView._enumItems.push(this);
} else {
this.ownerView._lazyAppend(aImmediateFlag, false, this._target);
this.ownerView._nonEnumItems.push(this);
}
},
@ -2674,8 +2777,10 @@ ViewHelpers.create({ constructor: Property, proto: Variable.prototype }, {
_onInit: function P__onInit(aImmediateFlag) {
if (this._initialDescriptor.enumerable) {
this.ownerView._lazyAppend(aImmediateFlag, true, this._target);
this.ownerView._enumItems.push(this);
} else {
this.ownerView._lazyAppend(aImmediateFlag, false, this._target);
this.ownerView._nonEnumItems.push(this);
}
}
});

View File

@ -663,9 +663,19 @@ var HeadsUpDisplayUICommands = {
let client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(() =>
client.listTabs((aResponse) =>
deferred.resolve({ form: aResponse, client: client })
));
client.listTabs((aResponse) => {
// Add Global Process debugging...
let globals = JSON.parse(JSON.stringify(aResponse));
delete globals.tabs;
delete globals.selected;
// ...only if there are appropriate actors (a 'from' property will
// always be there).
if (Object.keys(globals).length > 1) {
deferred.resolve({ form: globals, client: client, chrome: true });
} else {
deferred.reject("Global console not found!");
}
}));
return deferred.promise;
}

View File

@ -122,6 +122,7 @@ MOCHITEST_BROWSER_FILES = \
browser_console.js \
browser_longstring_hang.js \
browser_console_consolejsm_output.js \
browser_webconsole_bug_837351_securityerrors.js \
head.js \
$(NULL)
@ -224,6 +225,7 @@ MOCHITEST_BROWSER_FILES += \
test-bug-821877-csperrors.html^headers^ \
test-eval-in-stackframe.html \
test-bug-859170-longstring-hang.html \
test-bug-837351-security-errors.html \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -53,13 +53,13 @@ function testCSSPruning(hudRef) {
},
successFn: function()
{
is(Object.keys(hudRef.ui._cssNodes).length, LOG_LIMIT,
"repeated nodes pruned from cssNodes");
is(Object.keys(hudRef.ui._repeatNodes).length, LOG_LIMIT,
"repeated nodes pruned from repeatNodes");
let msg = hudRef.outputNode.querySelector(".webconsole-msg-cssparser " +
".webconsole-msg-repeat");
is(msg.getAttribute("value"), 1,
"repeated nodes pruned from cssNodes (confirmed)");
"repeated nodes pruned from repeatNodes (confirmed)");
finishTest();
},

View File

@ -0,0 +1,34 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_URI = "https://example.com/browser/browser/devtools/webconsole/test/test-bug-837351-security-errors.html";
function run_test()
{
addTab(TEST_URI);
browser.addEventListener("load", function onLoad(aEvent) {
browser.removeEventListener(aEvent.type, onLoad, true);
openConsole(null, function testSecurityErrorLogged (hud) {
let button = hud.ui.rootElement.querySelector(".webconsole-filter-button[category=\"security\"]");
ok(button, "Found security button in the web console");
waitForMessages({
webconsole: hud,
messages: [
{
name: "Logged blocking mixed active content",
text: "Blocked loading mixed active content \"http://example.com/\"",
category: CATEGORY_SECURITY,
severity: SEVERITY_ERROR
},
],
}).then(finishTest);
});
}, true);
}
function test()
{
SpecialPowers.pushPrefEnv({'set': [["security.mixed_content.block_active_content", true]]}, run_test);
}

View File

@ -25,6 +25,7 @@ const CATEGORY_JS = 2;
const CATEGORY_WEBDEV = 3;
const CATEGORY_INPUT = 4;
const CATEGORY_OUTPUT = 5;
const CATEGORY_SECURITY = 6;
// The possible message severities.
const SEVERITY_ERROR = 0;

View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
<meta charset="utf8">
<title>Mixed Content test - http on https</title>
<script src="testscript.js"></script>
<!--
- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/
-->
</head>
<body>
<iframe src = "http://example.com"></iframe>
</body>
</html>

View File

@ -81,6 +81,7 @@ const CATEGORY_JS = 2;
const CATEGORY_WEBDEV = 3;
const CATEGORY_INPUT = 4; // always on
const CATEGORY_OUTPUT = 5; // always on
const CATEGORY_SECURITY = 6;
// The possible message severities. As before, we start at zero so we can use
// these as indexes into MESSAGE_PREFERENCE_KEYS.
@ -97,6 +98,7 @@ const CATEGORY_CLASS_FRAGMENTS = [
"console",
"input",
"output",
"security",
];
// The fragment of a CSS class name that identifies each severity.
@ -120,6 +122,7 @@ const MESSAGE_PREFERENCE_KEYS = [
[ "error", "warn", "info", "log", ], // Web Developer
[ null, null, null, null, ], // Input
[ null, null, null, null, ], // Output
[ "secerror", "secwarn", null, null, ], // Security
];
// A mapping from the console API log event levels to the Web Console
@ -192,7 +195,7 @@ function WebConsoleFrame(aWebConsoleOwner)
this.owner = aWebConsoleOwner;
this.hudId = this.owner.hudId;
this._cssNodes = {};
this._repeatNodes = {};
this._outputQueue = [];
this._pruneCategoriesQueue = {};
this._networkRequests = {};
@ -289,11 +292,11 @@ WebConsoleFrame.prototype = {
_outputTimerInitialized: null,
/**
* Store for tracking repeated CSS nodes.
* Store for tracking repeated nodes.
* @private
* @type object
*/
_cssNodes: null,
_repeatNodes: null,
/**
* Preferences for filtering messages by type.
@ -509,6 +512,8 @@ WebConsoleFrame.prototype = {
info: Services.prefs.getBoolPref(FILTER_PREFS_PREFIX + "info"),
warn: Services.prefs.getBoolPref(FILTER_PREFS_PREFIX + "warn"),
log: Services.prefs.getBoolPref(FILTER_PREFS_PREFIX + "log"),
secerror: Services.prefs.getBoolPref(FILTER_PREFS_PREFIX + "secerror"),
secwarn: Services.prefs.getBoolPref(FILTER_PREFS_PREFIX + "secwarn"),
};
},
@ -898,10 +903,11 @@ WebConsoleFrame.prototype = {
let uid = repeatNode._uid;
let dupeNode = null;
if (aNode.classList.contains("webconsole-msg-cssparser")) {
dupeNode = this._cssNodes[uid];
if (aNode.classList.contains("webconsole-msg-cssparser") ||
aNode.classList.contains("webconsole-msg-security")) {
dupeNode = this._repeatNodes[uid];
if (!dupeNode) {
this._cssNodes[uid] = aNode;
this._repeatNodes[uid] = aNode;
}
}
else if (!aNode.classList.contains("webconsole-msg-network") &&
@ -1972,10 +1978,11 @@ WebConsoleFrame.prototype = {
aNode._objectActors.clear();
}
if (aNode.classList.contains("webconsole-msg-cssparser")) {
if (aNode.classList.contains("webconsole-msg-cssparser") ||
aNode.classList.contains("webconsole-msg-security")) {
let repeatNode = aNode.getElementsByClassName("webconsole-msg-repeat")[0];
if (repeatNode && repeatNode._uid) {
delete this._cssNodes[repeatNode._uid];
delete this._repeatNodes[repeatNode._uid];
}
}
else if (aNode._connectionId &&
@ -2583,7 +2590,7 @@ WebConsoleFrame.prototype = {
this._destroyer = Promise.defer();
this._cssNodes = {};
this._repeatNodes = {};
this._outputQueue = [];
this._pruneCategoriesQueue = {};
this._networkRequests = {};
@ -3598,7 +3605,7 @@ JSTerm.prototype = {
hud._outputQueue.forEach(hud._pruneItemFromQueue, hud);
hud._outputQueue = [];
hud._networkRequests = {};
hud._cssNodes = {};
hud._repeatNodes = {};
if (aClearStorage) {
this.webConsoleClient.clearMessagesCache();
@ -4259,9 +4266,9 @@ var Utils = {
*
* @param nsIScriptError aScriptError
* The script error you want to determine the category for.
* @return CATEGORY_JS|CATEGORY_CSS
* Depending on the script error CATEGORY_JS or CATEGORY_CSS can be
* returned.
* @return CATEGORY_JS|CATEGORY_CSS|CATEGORY_SECURITY
* Depending on the script error CATEGORY_JS, CATEGORY_CSS, or
* CATEGORY_SECURITY can be returned.
*/
categoryForScriptError: function Utils_categoryForScriptError(aScriptError)
{
@ -4270,6 +4277,9 @@ var Utils = {
case "CSS Loader":
return CATEGORY_CSS;
case "Mixed Content Blocker":
return CATEGORY_SECURITY;
default:
return CATEGORY_JS;
}

View File

@ -62,8 +62,8 @@
<menuitem id="menu_copyURL" label="&copyURLCmd.label;"
accesskey="&copyURLCmd.accesskey;" command="consoleCmd_copyURL"
selection="network" selectionType="single"/>
<menuitem id="menu_copy"/>
<menuitem id="menu_selectAll"/>
<menuitem id="cMenu_copy"/>
<menuitem id="cMenu_selectAll"/>
</menupopup>
</popupset>
@ -103,6 +103,16 @@
autocheck="false" prefKey="jswarn"/>
</menupopup>
</toolbarbutton>
<toolbarbutton label="&btnPageSecurity.label;" type="menu-button"
category="security" class="devtools-toolbarbutton webconsole-filter-button"
tooltiptext="&btnPageSecurity.tooltip;">
<menupopup>
<menuitem label="&btnConsoleErrors;" type="checkbox"
autocheck="false" prefKey="secerror"/>
<menuitem label="&btnConsoleWarnings;" type="checkbox"
autocheck="false" prefKey="secwarn"/>
</menupopup>
</toolbarbutton>
<toolbarbutton label="&btnPageLogging.label;" type="menu-button"
category="logging" class="devtools-toolbarbutton webconsole-filter-button"
tooltiptext="&btnPageLogging.tooltip;">

View File

@ -49,6 +49,8 @@
<!ENTITY btnPageCSS.tooltip "Log CSS parsing errors">
<!ENTITY btnPageJS.label "JS">
<!ENTITY btnPageJS.tooltip "Log JavaScript exceptions">
<!ENTITY btnPageSecurity.label "Security">
<!ENTITY btnPageSecurity.tooltip "Log security errors and warnings">
<!-- LOCALIZATION NOTE (btnPageLogging): This is used as the text of the
- the toolbar. It shows or hides messages that the web developer inserted on

View File

@ -402,22 +402,41 @@
<property name="suppressOnSelect"
onget="return this.getAttribute('suppressonselect') == 'true';"
onset="this.setAttribute('suppressonselect', val);"/>
<property name="crossSlideBoundary"
onget="return this.hasAttribute('crossslideboundary')? this.getAttribute('crossslideboundary') : Infinity;"/>
<!-- Internal methods -->
<field name="_xslideHandler"/>
<constructor>
<![CDATA[
if (this.controller && this.controller.gridBoundCallback != undefined)
this.controller.gridBoundCallback();
// set up cross-slide gesture handling for multiple-selection grids
if (CrossSlide && "multiple" == this.getAttribute("seltype")) {
this._xslideHandler = new CrossSlide.Handler(this, {
REARRANGESTART: this.crossSlideBoundary
});
this.addEventListener("touchstart", this._xslideHandler, false);
this.addEventListener("touchmove", this._xslideHandler, false);
this.addEventListener("touchend", this._xslideHandler, false);
}
// XXX This event was never actually implemented (bug 223411).
var event = document.createEvent("Events");
event.initEvent("contentgenerated", true, true);
this.dispatchEvent(event);
]]>
</constructor>
<method name="_isIndexInBounds">
<destructor>
<![CDATA[
if (this._xslideHandler) {
this.removeEventListener("touchstart", this._xslideHandler);
this.removeEventListener("touchmove", this._xslideHandler);
this.removeEventListener("touchend", this._xslideHandler);
this._xslideHandler = null;
}
]]>
</destructor>
<method name="_isIndexInBounds">
<parameter name="anIndex"/>
<body>
<![CDATA[
@ -484,6 +503,56 @@
}
]]>
</handler>
<handler event="MozCrossSliding">
<![CDATA[
// MozCrossSliding is swipe gesture across a tile
// The tile should follow the drag to reinforce the gesture
// (with inertia/speedbump behavior)
let state = event.crossSlidingState;
let thresholds = this._xslideHandler.thresholds;
let transformValue;
switch(state) {
case "cancelled":
// hopefully nothing else is transform-ing the tile
event.target.removeAttribute('crosssliding');
event.target.style.removeProperty('transform');
break;
case "dragging":
case "selecting":
event.target.setAttribute("crosssliding", true);
// just track the mouse in the initial phases of the drag gesture
transformValue = (event.direction=='x') ?
'translateX('+event.delta+'px)' :
'translateY('+event.delta+'px)';
event.target.style.transform = transformValue;
break;
case "selectSpeedBumping":
case "speedBumping":
event.target.setAttribute('crosssliding', true);
// in speed-bump phase, we add inertia to the drag
let offset = CrossSlide.speedbump(
event.delta,
thresholds.SPEEDBUMPSTART,
thresholds.SPEEDBUMPEND
);
transformValue = (event.direction=='x') ?
'translateX('+offset+'px)' :
'translateY('+offset+'px)';
event.target.style.transform = transformValue;
break;
// "rearranging" case not used or implemented here
case "completed":
event.target.removeAttribute('crosssliding');
event.target.style.removeProperty('transform');
break;
}
]]>
</handler>
<handler event="MozCrossSlideSelect">
<![CDATA[
this.toggleItemSelection(event.target);
]]>
</handler>
</handlers>
</binding>
<binding id="richgrid-item">

View File

@ -33,6 +33,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CrossSlide",
"resource:///modules/CrossSlide.jsm");
/*
* Services

View File

@ -335,7 +335,7 @@
</hbox>
</appbar>
<vbox id="panel-container" hidden="true" class="window-width window-height meta">
<vbox id="panel-container" hidden="true" class="window-width window-height meta" observes="bcast_windowState">
<hbox id="panel-header">
<toolbarbutton id="panel-close-button" command="cmd_panel"/>
@ -350,18 +350,18 @@
</menulist>
</hbox>
<deck id="panel-items" selectedIndex="0" flex="1">
<deck id="panel-items" selectedIndex="0" flex="1" >
<scrollbox id="bookmarks-container" flex="1">
<richgrid id="bookmarks-list" seltype="single" flex="1"/>
<richgrid id="bookmarks-list" class="canSnapTiles" seltype="single" flex="1"/>
</scrollbox>
<scrollbox id="history-container" flex="1">
<richgrid id="history-list" seltype="single" flex="1"/>
<richgrid id="history-list" class="canSnapTiles" seltype="single" flex="1"/>
</scrollbox>
<scrollbox id="downloads-container" flex="1">
<richgrid id="downloads-list" seltype="single" flex="1"/>
<richgrid id="downloads-list" class="canSnapTiles" seltype="single" flex="1"/>
</scrollbox>
<scrollbox id="remotetabs-container" flex="1">
<richgrid id="remotetabs-list" seltype="single" flex="1"/>
<richgrid id="remotetabs-list" class="canSnapTiles" seltype="single" flex="1"/>
</scrollbox>
<vbox id="console-container" flex="1">
<vbox id="console-header" class="panel-list">

View File

@ -0,0 +1,272 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["CrossSlide"];
// needs DPI adjustment?
let CrossSlideThresholds = {
SELECTIONSTART: 25,
SPEEDBUMPSTART: 30,
SPEEDBUMPEND: 50,
REARRANGESTART: 80
};
// see: http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.input.crossslidingstate.ASPx
let CrossSlidingState = {
STARTED: 0,
DRAGGING: 1,
SELECTING: 2,
SELECT_SPEED_BUMPING: 3,
SPEED_BUMPING: 4,
REARRANGING: 5,
COMPLETED: 6
};
let CrossSlidingStateNames = [
'started',
'dragging',
'selecting',
'selectSpeedBumping',
'speedBumping',
'rearranging',
'completed'
];
// --------------------------------
// module helpers
//
function isSelectable(aElement) {
// placeholder logic
return aElement.nodeName == 'richgriditem';
}
function withinCone(aLen, aHeight) {
// check pt falls within 45deg either side of the cross axis
return aLen > aHeight;
}
function getScrollAxisFromElement(aElement) {
let elem = aElement,
win = elem.ownerDocument.defaultView;
let scrollX, scrollY;
for (; elem && 1==elem.nodeType; elem = elem.parentNode) {
let cs = win.getComputedStyle(elem);
scrollX = (cs.overflowX=='scroll' || cs.overflowX=='auto');
scrollY = (cs.overflowX=='scroll' || cs.overflowX=='auto');
if (scrollX || scrollY) {
break;
}
}
return scrollX ? 'x' : 'y';
}
function pointFromTouchEvent(aEvent) {
let touch = aEvent.touches[0];
return { x: touch.clientX, y: touch.clientY };
}
// This damping function has these important properties:
// f(0) = 0
// f'(0) = 1
// limit as x -> Infinity of f(x) = 1
function damp(aX) {
return 2 / (1 + Math.exp(-2 * aX)) - 1;
}
function speedbump(aDelta, aStart, aEnd) {
let x = Math.abs(aDelta);
if (x <= aStart)
return aDelta;
let sign = aDelta / x;
let d = aEnd - aStart;
let damped = damp((x - aStart) / d);
return sign * (aStart + (damped * d));
}
this.CrossSlide = {
// -----------------------
// Gesture constants
Thresholds: CrossSlideThresholds,
State: CrossSlidingState,
StateNames: CrossSlidingStateNames,
// -----------------------
speedbump: speedbump
};
function CrossSlideHandler(aNode, aThresholds) {
this.node = aNode;
this.thresholds = Object.create(CrossSlideThresholds);
// apply per-instance threshold configuration
if (aThresholds) {
for(let key in aThresholds)
this.thresholds[key] = aThresholds[key];
}
}
CrossSlideHandler.prototype = {
node: null,
drag: null,
getCrossSlideState: function(aCrossAxisDistance, aScrollAxisDistance) {
if (aCrossAxisDistance <= 0) {
return CrossSlidingState.STARTED;
}
if (aCrossAxisDistance < this.thresholds.SELECTIONSTART) {
return CrossSlidingState.DRAGGING;
}
if (aCrossAxisDistance < this.thresholds.SPEEDBUMPSTART) {
return CrossSlidingState.SELECTING;
}
if (aCrossAxisDistance < this.thresholds.SPEEDBUMPEND) {
return CrossSlidingState.SELECT_SPEED_BUMPING;
}
if (aCrossAxisDistance < this.thresholds.REARRANGESTART) {
return CrossSlidingState.SPEED_BUMPING;
}
// out of bounds cross-slide
return -1;
},
handleEvent: function handleEvent(aEvent) {
switch (aEvent.type) {
case "touchstart":
this._onTouchStart(aEvent);
break;
case "touchmove":
this._onTouchMove(aEvent);
break;
case "touchend":
this._onTouchEnd(aEvent);
break;
}
},
cancel: function(){
this._fireProgressEvent("cancelled", aEvent);
this.drag = null;
},
_onTouchStart: function onTouchStart(aEvent){
if (aEvent.touches.length > 1)
return;
let touch = aEvent.touches[0];
// cross-slide is a single touch gesture
// the top target is the one we need here, touch.target not relevant
let target = aEvent.target;
if (!isSelectable(target))
return;
// we'll handle this event, dont let it bubble further
aEvent.stopPropagation();
let scrollAxis = getScrollAxisFromElement(target);
this.drag = {
scrollAxis: scrollAxis,
crossAxis: (scrollAxis=='x') ? 'y' : 'x',
origin: pointFromTouchEvent(aEvent),
state: -1
};
},
_onTouchMove: function(aEvent){
if (!this.drag) {
return;
}
// event is handled here, dont let it bubble further
aEvent.stopPropagation();
if (aEvent.touches.length!==1) {
// cancel if another touch point gets involved
return this.cancel();
}
let startPt = this.drag.origin;
let endPt = this.drag.position = pointFromTouchEvent(aEvent);
let scrollAxis = this.drag.scrollAxis,
crossAxis = this.drag.crossAxis;
// distance from the origin along the axis perpendicular to scrolling
let crossAxisDistance = Math.abs(endPt[crossAxis] - startPt[crossAxis]);
// distance along the scrolling axis
let scrollAxisDistance = Math.abs(endPt[scrollAxis] - startPt[scrollAxis]);
let currState = this.drag.state;
let newState = this.getCrossSlideState(crossAxisDistance, scrollAxisDistance);
if (-1 == newState) {
// out of bounds, cancel the event always
return this.cancel();
}
let isWithinCone = withinCone(crossAxisDistance, scrollAxisDistance);
if (currState < CrossSlidingState.SELECTING && !isWithinCone) {
// ignore, no progress to report
return;
}
if (currState >= CrossSlidingState.SELECTING && !isWithinCone) {
// we're committed to a cross-slide gesture,
// so going out of bounds at this point means aborting
return this.cancel();
}
if (currState > newState) {
// moved backwards, ignoring
return;
}
this.drag.state = newState;
this._fireProgressEvent( CrossSlidingStateNames[newState], aEvent );
},
_onTouchEnd: function(aEvent){
if (!this.drag)
return;
// event is handled, dont let it bubble further
aEvent.stopPropagation();
if (this.drag.state < CrossSlidingState.SELECTING) {
return this.cancel();
}
this._fireProgressEvent("completed", aEvent);
this._fireSelectEvent(aEvent);
this.drag = null;
},
/**
* Dispatches a custom Event on the drag node.
* @param aEvent The source event.
* @param aType The event type.
*/
_fireProgressEvent: function CrossSliding_fireEvent(aState, aEvent) {
if (!this.drag)
return;
let event = this.node.ownerDocument.createEvent("Events");
let crossAxis = this.drag.crossAxis;
event.initEvent("MozCrossSliding", true, true);
event.crossSlidingState = aState;
event.position = this.drag.position;
event.direction = this.drag.crossAxis;
event.delta = this.drag.position[crossAxis] - this.drag.origin[crossAxis];
aEvent.target.dispatchEvent(event);
},
/**
* Dispatches a custom Event on the given target node.
* @param aEvent The source event.
*/
_fireSelectEvent: function SelectTarget_fireEvent(aEvent) {
let event = this.node.ownerDocument.createEvent("Events");
event.initEvent("MozCrossSlideSelect", true, true);
event.position = this.drag.position;
aEvent.target.dispatchEvent(event);
}
};
this.CrossSlide.Handler = CrossSlideHandler;

View File

@ -9,9 +9,8 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/config.mk
EXTRA_JS_MODULES = \
colorUtils.jsm \
CrossSlide.jsm \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -894,6 +894,7 @@ setting[type="radio"] > vbox {
margin: 0px;
}
/* Browser Content Areas ----------------------------------------------------- */
/* Hide the browser while the start UI is visible */
@ -916,6 +917,10 @@ setting[type="radio"] > vbox {
padding: 60px 40px;
}
#panel-container[viewstate="snapped"] .canSnapTiles .richgrid-item-content {
-moz-box-orient: horizontal;
}
#panel-close-button {
background: transparent;
border: 0 none;
@ -944,11 +949,19 @@ setting[type="radio"] > vbox {
margin: 0;
}
#panel-container[viewstate="snapped"] #panel-view-switcher {
font-size: @metro_font_large@;
}
#panel-items {
padding-top: 20px;
-moz-padding-start: 88px;
}
#panel-container[viewstate="snapped"] #panel-items {
padding-left: 0px;
}
/* Console Section - Panel UI ---------------------------------------------- */
#console-filter-warnings,

View File

@ -490,6 +490,10 @@ richgriditem[selected] .richgrid-item-content::after {
background-size: 35px 35px;
border: @metro_border_xthick@ solid @selected_color@;
}
/* ease the return to original position when cross-sliding */
richgriditem:not([crosssliding]) {
transition: transform ease-out 0.2s;
}
richgriditem .richgrid-icon-container {
padding-bottom: 2px;

View File

@ -93,11 +93,13 @@
}
.requests-menu-file {
width: 14em;
width: 20vw;
min-width: 4em;
}
.requests-menu-domain {
width: 14em;
width: 14vw;
min-width: 10em;
}
.requests-menu-type {
@ -303,13 +305,44 @@
#details-pane {
max-width: none;
margin: 0 !important;
/* To prevent all the margin hacks to hide the sidebar */
/* To prevent all the margin hacks to hide the sidebar. */
}
.requests-menu-status-and-method {
width: 14vw;
}
.requests-menu-file,
.requests-menu-domain {
width: 30vw;
min-width: 10em;
}
.requests-menu-type {
width: 8vw;
}
.requests-menu-size {
border-width: 0px !important;
width: 16vw;
border-width: 0 !important;
box-shadow: none !important;
/* !important are required here because Timeline is not visible and thus
the right border and box-shadow of Size column should be hidden */
/* The "Timeline" header is not visible anymore, and thus the
right border and box-shadow of "Size" column should be hidden. */
}
}
@media (min-width: 701px) {
#network-table[type-overflows] .requests-menu-domain {
border-width: 0 !important;
box-shadow: none !important;
/* The "Type" header is not visible anymore, and thus the
right border and box-shadow of "Domain" column should be hidden. */
}
#network-table[domain-overflows] .requests-menu-file {
border-width: 0 !important;
box-shadow: none !important;
/* The "Domain" header is not visible anymore, and thus the
right border and box-shadow of "File" column should be hidden. */
}
}

View File

@ -240,3 +240,12 @@
height: 0;
border: none;
}
.webconsole-msg-security > .webconsole-msg-icon-container {
-moz-border-start: solid red 6px;
}
.webconsole-filter-button[category="security"] > .toolbarbutton-menubutton-button:before {
background-image: linear-gradient(#FF3030, #FF7D7D);
border-color: #D12C2C;
}

View File

@ -93,11 +93,13 @@
}
.requests-menu-file {
width: 16em;
width: 20vw;
min-width: 4em;
}
.requests-menu-domain {
width: 16em;
width: 14vw;
min-width: 10em;
}
.requests-menu-type {
@ -303,13 +305,44 @@
#details-pane {
max-width: none;
margin: 0 !important;
/* To prevent all the margin hacks to hide the sidebar */
/* To prevent all the margin hacks to hide the sidebar. */
}
.requests-menu-status-and-method {
width: 14vw;
}
.requests-menu-file,
.requests-menu-domain {
width: 30vw;
min-width: 10em;
}
.requests-menu-type {
width: 8vw;
}
.requests-menu-size {
border-width: 0px !important;
width: 16vw;
border-width: 0 !important;
box-shadow: none !important;
/* !important are required here because Timeline is not visible and thus
the right border and box-shadow of Size column should be hidden */
/* The "Timeline" header is not visible anymore, and thus the
right border and box-shadow of "Size" column should be hidden. */
}
}
@media (min-width: 701px) {
#network-table[type-overflows] .requests-menu-domain {
border-width: 0 !important;
box-shadow: none !important;
/* The "Type" header is not visible anymore, and thus the
right border and box-shadow of "Domain" column should be hidden. */
}
#network-table[domain-overflows] .requests-menu-file {
border-width: 0 !important;
box-shadow: none !important;
/* The "Domain" header is not visible anymore, and thus the
right border and box-shadow of "File" column should be hidden. */
}
}

View File

@ -244,3 +244,12 @@
height: 0;
border: none;
}
.webconsole-msg-security > .webconsole-msg-icon-container {
-moz-border-start: solid red 6px;
}
.webconsole-filter-button[category="security"] > .toolbarbutton-menubutton-button:before {
background-image: linear-gradient(#FF3030, #FF7D7D);
border-color: #D12C2C;
}

View File

@ -93,11 +93,13 @@
}
.requests-menu-file {
width: 16em;
width: 20vw;
min-width: 4em;
}
.requests-menu-domain {
width: 16em;
width: 14vw;
min-width: 10em;
}
.requests-menu-type {
@ -303,13 +305,44 @@
#details-pane {
max-width: none;
margin: 0 !important;
/* To prevent all the margin hacks to hide the sidebar */
/* To prevent all the margin hacks to hide the sidebar. */
}
.requests-menu-status-and-method {
width: 14vw;
}
.requests-menu-file,
.requests-menu-domain {
width: 30vw;
min-width: 10em;
}
.requests-menu-type {
width: 8vw;
}
.requests-menu-size {
border-width: 0px !important;
width: 16vw;
border-width: 0 !important;
box-shadow: none !important;
/* !important are required here because Timeline is not visible and thus
the right border and box-shadow of Size column should be hidden */
/* The "Timeline" header is not visible anymore, and thus the
right border and box-shadow of "Size" column should be hidden. */
}
}
@media (min-width: 701px) {
#network-table[type-overflows] .requests-menu-domain {
border-width: 0 !important;
box-shadow: none !important;
/* The "Type" header is not visible anymore, and thus the
right border and box-shadow of "Domain" column should be hidden. */
}
#network-table[domain-overflows] .requests-menu-file {
border-width: 0 !important;
box-shadow: none !important;
/* The "Domain" header is not visible anymore, and thus the
right border and box-shadow of "File" column should be hidden. */
}
}

View File

@ -249,3 +249,12 @@
height: 0;
border: none;
}
.webconsole-msg-security > .webconsole-msg-icon-container {
-moz-border-start: solid red 6px;
}
.webconsole-filter-button[category="security"] > .toolbarbutton-menubutton-button:before {
background-image: linear-gradient(#FF3030, #FF7D7D);
border-color: #D12C2C;
}

View File

@ -53,7 +53,7 @@ netscape_security_enablePrivilege(JSContext *cx, unsigned argc, JS::Value *vp)
return xpc::EnableUniversalXPConnect(cx);
}
static JSFunctionSpec PrivilegeManager_static_methods[] = {
static const JSFunctionSpec PrivilegeManager_static_methods[] = {
JS_FS("enablePrivilege", netscape_security_enablePrivilege, 1, 0),
JS_FS_END
};

View File

@ -1073,6 +1073,8 @@ conic/conicstatisticsevent.h
#endif
#if MOZ_NATIVE_LIBEVENT==1
event.h
#else
sys/event.h
#endif
#ifdef MOZ_ENABLE_LIBPROXY
proxy.h

View File

@ -5186,23 +5186,6 @@ fi
AC_SUBST(MOZ_IPDL_TESTS)
dnl ========================================================
dnl = Turns off code necessary for e10s compatibility
dnl ========================================================
dnl This is a temporary flag to be removed in bug 662601 when
dnl it's no longer needed
MOZ_E10S_COMPAT=
MOZ_ARG_ENABLE_BOOL(e10s-compat,
[ --enable-e10s-compat Turns off code for e10s compat],
MOZ_E10S_COMPAT=1,
MOZ_E10S_COMPAT=)
if test -n "$MOZ_E10S_COMPAT"; then
AC_DEFINE(MOZ_E10S_COMPAT)
fi
dnl ========================================================
dnl = Disable building dbm
dnl ========================================================

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script>
function boom()
{
document.setUserData('key', {}, null);
// Depending on the pref bidi.direction, one of these will hit the page scrollbar.
window.cpRight = document.caretPositionFromPoint(window.innerWidth - 1, 0);
window.cpLeft = document.caretPositionFromPoint(0, 0);
}
</script>
</head>
<body onload="boom();">
<div style="height: 50000px;"></div>
</body>
</html>

View File

@ -134,3 +134,4 @@ load 844404.html
load 847127.html
load 849601.html
load 863950.html
load 864448.html

View File

@ -795,6 +795,7 @@ public:
eBRAND_PROPERTIES,
eCOMMON_DIALOG_PROPERTIES,
eMATHML_PROPERTIES,
eSECURITY_PROPERTIES,
PropertiesFile_COUNT
};
static nsresult ReportToConsole(uint32_t aErrorFlags,

View File

@ -23,7 +23,6 @@
#include "nsPIDOMWindow.h" // for use in inline functions
#include "nsPropertyTable.h" // for member
#include "nsTHashtable.h" // for member
#include "mozilla/dom/DirectionalityUtils.h"
#include "mozilla/dom/DocumentBinding.h"
class imgIRequest;
@ -523,10 +522,6 @@ public:
mSandboxFlags = sandboxFlags;
}
inline mozilla::Directionality GetDocumentDirectionality() {
return mDirectionality;
}
/**
* Access HTTP header data (this may also get set from other
* sources, like HTML META tags).
@ -641,7 +636,7 @@ protected:
public:
// Get the root <html> element, or return null if there isn't one (e.g.
// if the root isn't <html>)
Element* GetHtmlElement();
Element* GetHtmlElement() const;
// Returns the first child of GetHtmlContent which has the given tag,
// or nullptr if that doesn't exist.
Element* GetHtmlChildElement(nsIAtom* aTag);
@ -2001,7 +1996,7 @@ public:
virtual void GetTitle(nsString& aTitle) = 0;
virtual void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv) = 0;
void GetDir(nsAString& aDirection) const;
void SetDir(const nsAString& aDirection, mozilla::ErrorResult& rv);
void SetDir(const nsAString& aDirection);
nsIDOMWindow* GetDefaultView() const
{
return GetWindow();
@ -2150,12 +2145,6 @@ protected:
return mContentType;
}
inline void
SetDocumentDirectionality(mozilla::Directionality aDir)
{
mDirectionality = aDir;
}
// All document WrapNode implementations MUST call this method. A
// false return value means an exception was thrown.
bool PostCreateWrapper(JSContext* aCx, JSObject *aNewObject);
@ -2335,9 +2324,6 @@ protected:
// are immutable - see nsSandboxFlags.h for the possible flags.
uint32_t mSandboxFlags;
// The root directionality of this document.
mozilla::Directionality mDirectionality;
nsCString mContentLanguage;
private:
nsCString mContentType;

View File

@ -600,13 +600,9 @@ RecomputeDirectionality(Element* aElement, bool aNotify)
dir = parentDir;
}
} else {
// If there is no parent element, the directionality is the same as the
// document direction.
Directionality documentDir =
aElement->OwnerDoc()->GetDocumentDirectionality();
if (documentDir != eDir_NotSet) {
dir = documentDir;
}
// If there is no parent element and no dir attribute, the directionality
// is LTR.
dir = eDir_LTR;
}
aElement->SetDirectionality(dir, aNotify);

View File

@ -3224,7 +3224,8 @@ static const char gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT][56] = {
"chrome://global/locale/svg/svg.properties",
"chrome://branding/locale/brand.properties",
"chrome://global/locale/commonDialogs.properties",
"chrome://global/locale/mathml/mathml.properties"
"chrome://global/locale/mathml/mathml.properties",
"chrome://global/locale/security/security.properties"
};
/* static */ nsresult

View File

@ -58,7 +58,8 @@ nsDOMCaretPosition::WrapObject(JSContext *aCx, JSObject *aScope)
return mozilla::dom::CaretPositionBinding::Wrap(aCx, aScope, this);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCaretPosition, mOffsetNode)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(nsDOMCaretPosition,
mOffsetNode, mAnonymousContentNode)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCaretPosition)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCaretPosition)

View File

@ -142,8 +142,6 @@ nsDOMDataChannel::Init(nsPIDOMWindow* aDOMWindow)
nsresult rv;
nsAutoString urlParam;
nsDOMEventTargetHelper::Init();
MOZ_ASSERT(mDataChannel);
mDataChannel->SetListener(this, nullptr);

View File

@ -118,20 +118,25 @@ nsDOMFileReader::~nsDOMFileReader()
nsLayoutStatics::Release();
}
/**
* This Init method is called from the factory constructor.
*/
nsresult
nsDOMFileReader::Init()
{
nsDOMEventTargetHelper::Init();
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
nsCOMPtr<nsIPrincipal> subjectPrincipal;
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
nsCOMPtr<nsIPrincipal> principal;
if (secMan) {
nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
secMan->GetSystemPrincipal(getter_AddRefs(principal));
}
NS_ENSURE_STATE(subjectPrincipal);
mPrincipal.swap(subjectPrincipal);
NS_ENSURE_STATE(principal);
mPrincipal.swap(principal);
// Instead of grabbing some random global from the context stack,
// let's use the default one (junk drawer) for now.
// We should move away from this Init...
BindToOwner(xpc::GetNativeForGlobal(xpc::GetJunkScope()));
return NS_OK;
}

View File

@ -94,7 +94,6 @@
#include "nsIFormControl.h"
#include "nsBidiUtils.h"
#include "mozilla/dom/DirectionalityUtils.h"
#include "nsIDOMUserDataHandler.h"
#include "nsIDOMXPathEvaluator.h"
@ -1323,7 +1322,6 @@ nsIDocument::nsIDocument()
mAllowDNSPrefetch(true),
mIsBeingUsedAsImage(false),
mHasLinksToUpdate(false),
mDirectionality(eDir_LTR),
mPartID(0)
{
SetInDocument();
@ -5782,7 +5780,7 @@ nsIDocument::GetLocation() const
}
Element*
nsIDocument::GetHtmlElement()
nsIDocument::GetHtmlElement() const
{
Element* rootElement = GetRootElement();
if (rootElement && rootElement->IsHTML(nsGkAtoms::html))
@ -6241,17 +6239,6 @@ nsDocument::GetAnimationController()
return mAnimationController;
}
struct DirTable {
const char* mName;
uint8_t mValue;
};
static const DirTable dirAttributes[] = {
{"ltr", IBMBIDI_TEXTDIRECTION_LTR},
{"rtl", IBMBIDI_TEXTDIRECTION_RTL},
{0}
};
/**
* Retrieve the "direction" property of the document.
*
@ -6267,12 +6254,10 @@ nsDocument::GetDir(nsAString& aDirection)
void
nsIDocument::GetDir(nsAString& aDirection) const
{
uint32_t options = GetBidiOptions();
for (const DirTable* elt = dirAttributes; elt->mName; elt++) {
if (GET_BIDI_OPTION_DIRECTION(options) == elt->mValue) {
CopyASCIItoUTF16(elt->mName, aDirection);
return;
}
aDirection.Truncate();
Element* rootElement = GetHtmlElement();
if (rootElement) {
static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
}
}
@ -6284,45 +6269,17 @@ nsIDocument::GetDir(nsAString& aDirection) const
NS_IMETHODIMP
nsDocument::SetDir(const nsAString& aDirection)
{
ErrorResult rv;
nsIDocument::SetDir(aDirection, rv);
return rv.ErrorCode();
nsIDocument::SetDir(aDirection);
return NS_OK;
}
void
nsIDocument::SetDir(const nsAString& aDirection, ErrorResult& rv)
nsIDocument::SetDir(const nsAString& aDirection)
{
uint32_t options = GetBidiOptions();
for (const DirTable* elt = dirAttributes; elt->mName; elt++) {
if (aDirection == NS_ConvertASCIItoUTF16(elt->mName)) {
if (GET_BIDI_OPTION_DIRECTION(options) != elt->mValue) {
SET_BIDI_OPTION_DIRECTION(options, elt->mValue);
nsIPresShell *shell = GetShell();
if (shell) {
nsPresContext *context = shell->GetPresContext();
if (!context) {
rv.Throw(NS_ERROR_UNEXPECTED);
return;
}
context->SetBidi(options, true);
} else {
// No presentation; just set it on ourselves
SetBidiOptions(options);
}
Directionality dir = elt->mValue == IBMBIDI_TEXTDIRECTION_RTL ?
eDir_RTL : eDir_LTR;
SetDocumentDirectionality(dir);
// Set the directionality of the root element and its descendants, if any
Element* rootElement = GetRootElement();
if (rootElement) {
rootElement->SetDirectionality(dir, true);
SetDirectionalityOnDescendants(rootElement, dir);
}
}
break;
}
Element* rootElement = GetHtmlElement();
if (rootElement) {
rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
aDirection, true);
}
}

View File

@ -25,6 +25,7 @@
#include "nsIDocumentLoader.h"
#include "nsIWebNavigation.h"
#include "nsLoadGroup.h"
#include "nsIScriptError.h"
#include "prlog.h"
@ -146,6 +147,24 @@ nsMixedContentBlocker::~nsMixedContentBlocker()
NS_IMPL_ISUPPORTS1(nsMixedContentBlocker, nsIContentPolicy)
void
LogBlockingMixedContent(MixedContentTypes classification,
nsIURI* aContentLocation,
nsIDocument* aRootDoc)
{
nsAutoCString locationSpec;
aContentLocation->GetSpec(locationSpec);
NS_ConvertUTF8toUTF16 locationSpecUTF16(locationSpec);
const PRUnichar* strings[] = { locationSpecUTF16.get() };
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
"Mixed Content Blocker",
aRootDoc,
nsContentUtils::eSECURITY_PROPERTIES,
classification == eMixedDisplay ? "BlockMixedDisplayContent" : "BlockMixedActiveContent",
strings, ArrayLength(strings));
}
NS_IMETHODIMP
nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
nsIURI* aContentLocation,
@ -441,6 +460,7 @@ nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
}
} else {
*aDecision = nsIContentPolicy::REJECT_REQUEST;
LogBlockingMixedContent(classification, aContentLocation, rootDoc);
if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) {
eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
}
@ -481,6 +501,7 @@ nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
} else {
//User has not overriden the pref by Disabling protection. Reject the request and update the security state.
*aDecision = nsIContentPolicy::REJECT_REQUEST;
LogBlockingMixedContent(classification, aContentLocation, rootDoc);
// See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
if (rootDoc->GetHasMixedActiveContentBlocked()) {
return NS_OK;

View File

@ -363,27 +363,3 @@ nsDOMEventTargetHelper::GetContextForEventHandlers(nsresult* aRv)
: nullptr;
}
void
nsDOMEventTargetHelper::Init(JSContext* aCx)
{
JSContext* cx = aCx;
if (!cx) {
nsIJSContextStack* stack = nsContentUtils::ThreadJSContextStack();
if (!stack)
return;
if (NS_FAILED(stack->Peek(&cx)) || !cx)
return;
}
NS_ASSERTION(cx, "Should have returned earlier ...");
nsIScriptContext* context = GetScriptContextFromJSContext(cx);
if (context) {
nsCOMPtr<nsPIDOMWindow> window =
do_QueryInterface(context->GetGlobalObject());
if (window) {
BindToOwner(window->GetCurrentInnerWindow());
}
}
}

View File

@ -68,8 +68,6 @@ public:
return static_cast<nsDOMEventTargetHelper*>(target);
}
void Init(JSContext* aCx = nullptr);
bool HasListenersFor(nsIAtom* aTypeWithOn)
{
return mListenerManager && mListenerManager->HasListenersFor(aTypeWithOn);

View File

@ -4561,7 +4561,9 @@ nsEventStateManager::CheckForAndDispatchClick(nsPresContext* aPresContext,
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
if (presShell) {
nsCOMPtr<nsIContent> mouseContent = GetEventTargetContent(aEvent);
if (!mouseContent && !mCurrentTarget) {
return NS_OK;
}
ret = presShell->HandleEventWithTarget(&event, mCurrentTarget,
mouseContent, aStatus);
if (NS_SUCCEEDED(ret) && aEvent->clickCount == 2) {

View File

@ -956,7 +956,7 @@ protected:
nsAutoPtr<AudioStream> mAudioStream;
// Range of time played.
TimeRanges mPlayed;
nsRefPtr<TimeRanges> mPlayed;
// Stores the time at the start of the current 'played' range.
double mCurrentPlayRangeStart;

View File

@ -411,6 +411,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTM
for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputStreams[i].mStream);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlayed);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
@ -427,6 +428,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLE
for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputStreams[i].mStream);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
@ -1307,7 +1309,7 @@ HTMLMediaElement::SetCurrentTime(double aCurrentTime, ErrorResult& aRv)
LOG(PR_LOG_DEBUG, ("%p Adding \'played\' a range : [%f, %f]", this, mCurrentPlayRangeStart, rangeEndTime));
// Multiple seek without playing, or seek while playing.
if (mCurrentPlayRangeStart != rangeEndTime) {
mPlayed.Add(mCurrentPlayRangeStart, rangeEndTime);
mPlayed->Add(mCurrentPlayRangeStart, rangeEndTime);
}
}
@ -1408,12 +1410,12 @@ HTMLMediaElement::Played()
nsRefPtr<TimeRanges> ranges = new TimeRanges();
uint32_t timeRangeCount = 0;
mPlayed.GetLength(&timeRangeCount);
mPlayed->GetLength(&timeRangeCount);
for (uint32_t i = 0; i < timeRangeCount; i++) {
double begin;
double end;
mPlayed.Start(i, &begin);
mPlayed.End(i, &end);
mPlayed->Start(i, &begin);
mPlayed->End(i, &end);
ranges->Add(begin, end);
}
@ -1888,6 +1890,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
mDefaultPlaybackRate(1.0),
mPlaybackRate(1.0),
mPreservesPitch(true),
mPlayed(new TimeRanges),
mCurrentPlayRangeStart(-1.0),
mAllowAudioData(false),
mBegun(false),

View File

@ -377,10 +377,12 @@ ImageDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject)
target->AddEventListener(NS_LITERAL_STRING("resize"), this, false);
target->AddEventListener(NS_LITERAL_STRING("keypress"), this, false);
if (!nsContentUtils::IsChildOfSameType(this) &&
GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) {
LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/TopLevelImageDocument.css"));
LinkStylesheet(NS_LITERAL_STRING("chrome://global/skin/media/TopLevelImageDocument.css"));
if (GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) {
LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/ImageDocument.css"));
if (!nsContentUtils::IsChildOfSameType(this)) {
LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/TopLevelImageDocument.css"));
LinkStylesheet(NS_LITERAL_STRING("chrome://global/skin/media/TopLevelImageDocument.css"));
}
}
BecomeInteractive();
}
@ -810,29 +812,6 @@ ImageDocument::CreateSyntheticDocument()
nsresult rv = MediaDocument::CreateSyntheticDocument();
NS_ENSURE_SUCCESS(rv, rv);
// We must declare the image as a block element. If we stay as
// an inline element, our parent LineBox will be inline too and
// ignore the available height during reflow.
// This is bad during printing, it means tall image frames won't know
// the size of the paper and cannot break into continuations along
// multiple pages.
Element* head = GetHeadElement();
NS_ENSURE_TRUE(head, NS_ERROR_FAILURE);
nsCOMPtr<nsINodeInfo> nodeInfo;
if (nsContentUtils::IsChildOfSameType(this)) {
nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::style, nullptr,
kNameSpaceID_XHTML,
nsIDOMNode::ELEMENT_NODE);
nsRefPtr<nsGenericHTMLElement> styleContent = NS_NewHTMLStyleElement(nodeInfo.forget());
NS_ENSURE_TRUE(styleContent, NS_ERROR_OUT_OF_MEMORY);
ErrorResult error;
styleContent->SetTextContent(NS_LITERAL_STRING("img { display: block; }"),
error);
head->AppendChildTo(styleContent, false);
}
// Add the image element
Element* body = GetBodyElement();
if (!body) {
@ -840,6 +819,7 @@ ImageDocument::CreateSyntheticDocument()
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsINodeInfo> nodeInfo;
nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nullptr,
kNameSpaceID_XHTML,
nsIDOMNode::ELEMENT_NODE);

View File

@ -201,6 +201,11 @@ public:
}
}
void CancelAllEvents()
{
mEvents.Clear();
}
// This method computes the AudioParam value at a given time based on the event timeline
template<class TimeType>
float GetValueAtTime(TimeType aTime) const

View File

@ -77,11 +77,6 @@ AnalyserNode::AnalyserNode(AudioContext* aContext)
AllocateBuffer();
}
AnalyserNode::~AnalyserNode()
{
DestroyMediaStream();
}
JSObject*
AnalyserNode::WrapObject(JSContext* aCx, JSObject* aScope)
{
@ -243,12 +238,6 @@ AnalyserNode::ApplyBlackmanWindow(float* aBuffer, uint32_t aSize)
}
}
void
AnalyserNode::DestroyMediaStream()
{
AudioNode::DestroyMediaStream();
}
bool
AnalyserNode::AllocateBuffer()
{

View File

@ -18,7 +18,6 @@ class AnalyserNode : public AudioNode
{
public:
explicit AnalyserNode(AudioContext* aContext);
virtual ~AnalyserNode();
NS_DECL_ISUPPORTS_INHERITED
@ -29,8 +28,6 @@ public:
return true;
}
virtual void DestroyMediaStream() MOZ_OVERRIDE;
void GetFloatFrequencyData(Float32Array& aArray);
void GetByteFrequencyData(Uint8Array& aArray);
void GetByteTimeDomainData(Uint8Array& aArray);

View File

@ -165,8 +165,7 @@ StealJSArrayDataIntoThreadSharedFloatArrayBufferList(JSContext* aJSContext,
}
ThreadSharedFloatArrayBufferList*
AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext, uint32_t* aRate,
uint32_t* aLength)
AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext)
{
if (!mSharedChannels) {
// Steal JS data
@ -174,8 +173,6 @@ AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext, uint32_t* aRa
StealJSArrayDataIntoThreadSharedFloatArrayBufferList(aJSContext, mJSChannels);
}
*aLength = mLength;
*aRate = mSampleRate;
return mSharedChannels;
}

View File

@ -89,12 +89,9 @@ public:
}
/**
* Returns a ThreadSharedFloatArrayBufferList containing the sample data
* at aRate. Sets *aLength to the number of samples per channel.
* Returns a ThreadSharedFloatArrayBufferList containing the sample data.
*/
ThreadSharedFloatArrayBufferList* GetThreadSharedChannelsForRate(JSContext* aContext,
uint32_t* aRate,
uint32_t* aLength);
ThreadSharedFloatArrayBufferList* GetThreadSharedChannelsForRate(JSContext* aContext);
// aContents should either come from JS_AllocateArrayBufferContents or
// JS_StealArrayBufferContents.

View File

@ -58,25 +58,10 @@ public:
}
}
// START, OFFSET and DURATION are always set by start() (along with setting
// mBuffer to something non-null).
// STOP is set by stop().
enum Parameters {
SAMPLE_RATE,
START,
STOP,
OFFSET,
DURATION,
LOOP,
LOOPSTART,
LOOPEND,
PLAYBACKRATE,
DOPPLERSHIFT
};
virtual void SetTimelineParameter(uint32_t aIndex, const dom::AudioParamTimeline& aValue)
{
switch (aIndex) {
case PLAYBACKRATE:
case AudioBufferSourceNode::PLAYBACKRATE:
mPlaybackRateTimeline = aValue;
// If we have a simple value that is 1.0 (i.e. intrinsic speed), and our
// input buffer is already at the ideal audio rate, and we have a
@ -96,8 +81,8 @@ public:
virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam)
{
switch (aIndex) {
case START: mStart = aParam; break;
case STOP: mStop = aParam; break;
case AudioBufferSourceNode::START: mStart = aParam; break;
case AudioBufferSourceNode::STOP: mStop = aParam; break;
default:
NS_ERROR("Bad AudioBufferSourceNodeEngine StreamTimeParameter");
}
@ -105,7 +90,7 @@ public:
virtual void SetDoubleParameter(uint32_t aIndex, double aParam)
{
switch (aIndex) {
case DOPPLERSHIFT:
case AudioBufferSourceNode::DOPPLERSHIFT:
mDopplerShift = aParam;
break;
default:
@ -115,12 +100,12 @@ public:
virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam)
{
switch (aIndex) {
case SAMPLE_RATE: mSampleRate = aParam; break;
case OFFSET: mOffset = aParam; break;
case DURATION: mDuration = aParam; break;
case LOOP: mLoop = !!aParam; break;
case LOOPSTART: mLoopStart = aParam; break;
case LOOPEND: mLoopEnd = aParam; break;
case AudioBufferSourceNode::SAMPLE_RATE: mSampleRate = aParam; break;
case AudioBufferSourceNode::OFFSET: mOffset = aParam; break;
case AudioBufferSourceNode::DURATION: mDuration = aParam; break;
case AudioBufferSourceNode::LOOP: mLoop = !!aParam; break;
case AudioBufferSourceNode::LOOPSTART: mLoopStart = aParam; break;
case AudioBufferSourceNode::LOOPEND: mLoopEnd = aParam; break;
default:
NS_ERROR("Bad AudioBufferSourceNodeEngine Int32Parameter");
}
@ -428,11 +413,9 @@ AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* aContext)
AudioBufferSourceNode::~AudioBufferSourceNode()
{
//
if (Context()) {
Context()->UnregisterAudioBufferSourceNode(this);
}
DestroyMediaStream();
}
JSObject*
@ -457,10 +440,10 @@ AudioBufferSourceNode::Start(JSContext* aCx, double aWhen, double aOffset,
return;
}
uint32_t rate;
uint32_t lengthSamples;
float rate = mBuffer->SampleRate();
int32_t lengthSamples = mBuffer->Length();
nsRefPtr<ThreadSharedFloatArrayBufferList> data =
mBuffer->GetThreadSharedChannelsForRate(aCx, &rate, &lengthSamples);
mBuffer->GetThreadSharedChannelsForRate(aCx);
double length = double(lengthSamples) / rate;
double offset = std::max(0.0, aOffset);
double endOffset = aDuration.WasPassed() ?
@ -470,40 +453,19 @@ AudioBufferSourceNode::Start(JSContext* aCx, double aWhen, double aOffset,
return;
}
// Don't compute and set the loop parameters unnecessarily
if (mLoop) {
double actualLoopStart, actualLoopEnd;
if (((mLoopStart != 0.0) || (mLoopEnd != 0.0)) &&
mLoopStart >= 0.0 && mLoopEnd > 0.0 &&
mLoopStart < mLoopEnd) {
actualLoopStart = (mLoopStart > length) ? 0.0 : mLoopStart;
actualLoopEnd = std::min(mLoopEnd, length);
} else {
actualLoopStart = 0.0;
actualLoopEnd = length;
}
int32_t loopStartTicks = NS_lround(actualLoopStart * rate);
int32_t loopEndTicks = NS_lround(actualLoopEnd * rate);
ns->SetInt32Parameter(AudioBufferSourceNodeEngine::LOOP, 1);
ns->SetInt32Parameter(AudioBufferSourceNodeEngine::LOOPSTART, loopStartTicks);
ns->SetInt32Parameter(AudioBufferSourceNodeEngine::LOOPEND, loopEndTicks);
}
ns->SetBuffer(data.forget());
// Don't set parameter unnecessarily
if (aWhen > 0.0) {
ns->SetStreamTimeParameter(AudioBufferSourceNodeEngine::START,
Context()->DestinationStream(),
aWhen);
ns->SetStreamTimeParameter(START, Context()->DestinationStream(), aWhen);
}
int32_t offsetTicks = NS_lround(offset*rate);
// Don't set parameter unnecessarily
if (offsetTicks > 0) {
ns->SetInt32Parameter(AudioBufferSourceNodeEngine::OFFSET, offsetTicks);
ns->SetInt32Parameter(OFFSET, offsetTicks);
}
ns->SetInt32Parameter(AudioBufferSourceNodeEngine::DURATION,
ns->SetInt32Parameter(DURATION,
NS_lround(endOffset*rate) - offsetTicks);
ns->SetInt32Parameter(AudioBufferSourceNodeEngine::SAMPLE_RATE, rate);
ns->SetInt32Parameter(SAMPLE_RATE, rate);
MOZ_ASSERT(!mPlayingRef, "We can only accept a successful start() call once");
mPlayingRef.Take(this);
@ -523,8 +485,7 @@ AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
return;
}
ns->SetStreamTimeParameter(AudioBufferSourceNodeEngine::STOP,
Context()->DestinationStream(),
ns->SetStreamTimeParameter(STOP, Context()->DestinationStream(),
std::max(0.0, aWhen));
}
@ -542,13 +503,39 @@ void
AudioBufferSourceNode::SendPlaybackRateToStream(AudioNode* aNode)
{
AudioBufferSourceNode* This = static_cast<AudioBufferSourceNode*>(aNode);
SendTimelineParameterToStream(This, AudioBufferSourceNodeEngine::PLAYBACKRATE, *This->mPlaybackRate);
SendTimelineParameterToStream(This, PLAYBACKRATE, *This->mPlaybackRate);
}
void
AudioBufferSourceNode::SendDopplerShiftToStream(double aDopplerShift)
{
SendDoubleParameterToStream(AudioBufferSourceNodeEngine::DOPPLERSHIFT, aDopplerShift);
SendDoubleParameterToStream(DOPPLERSHIFT, aDopplerShift);
}
void
AudioBufferSourceNode::SendLoopParametersToStream()
{
SendInt32ParameterToStream(LOOP, mLoop ? 1 : 0);
// Don't compute and set the loop parameters unnecessarily
if (mLoop && mBuffer) {
float rate = mBuffer->SampleRate();
double length = (double(mBuffer->Length()) / mBuffer->SampleRate());
double actualLoopStart, actualLoopEnd;
if (((mLoopStart != 0.0) || (mLoopEnd != 0.0)) &&
mLoopStart >= 0.0 && mLoopEnd > 0.0 &&
mLoopStart < mLoopEnd) {
actualLoopStart = (mLoopStart > length) ? 0.0 : mLoopStart;
actualLoopEnd = std::min(mLoopEnd, length);
} else {
actualLoopStart = 0.0;
actualLoopEnd = length;
}
int32_t loopStartTicks = NS_lround(actualLoopStart * rate);
int32_t loopEndTicks = NS_lround(actualLoopEnd * rate);
SendInt32ParameterToStream(LOOPSTART, loopStartTicks);
SendInt32ParameterToStream(LOOPEND, loopEndTicks);
}
}
}

View File

@ -93,6 +93,7 @@ public:
void SetLoop(bool aLoop)
{
mLoop = aLoop;
SendLoopParametersToStream();
}
double LoopStart() const
{
@ -101,6 +102,7 @@ public:
void SetLoopStart(double aStart)
{
mLoopStart = aStart;
SendLoopParametersToStream();
}
double LoopEnd() const
{
@ -109,13 +111,34 @@ public:
void SetLoopEnd(double aEnd)
{
mLoopEnd = aEnd;
SendLoopParametersToStream();
}
void SendDopplerShiftToStream(double aDopplerShift);
virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
private:
friend class AudioBufferSourceNodeEngine;
// START, OFFSET and DURATION are always set by start() (along with setting
// mBuffer to something non-null).
// STOP is set by stop().
enum EngineParameters {
SAMPLE_RATE,
START,
STOP,
OFFSET,
DURATION,
LOOP,
LOOPSTART,
LOOPEND,
PLAYBACKRATE,
DOPPLERSHIFT
};
void SendLoopParametersToStream();
static void SendPlaybackRateToStream(AudioNode* aNode);
private:
double mLoopStart;
double mLoopEnd;
nsRefPtr<AudioBuffer> mBuffer;

View File

@ -141,6 +141,7 @@ AudioContext::CreateScriptProcessor(uint32_t aBufferSize,
nsRefPtr<ScriptProcessorNode> scriptProcessor =
new ScriptProcessorNode(this, aBufferSize, aNumberOfInputChannels,
aNumberOfOutputChannels);
mScriptProcessorNodes.AppendElement(scriptProcessor);
return scriptProcessor.forget();
}
@ -245,6 +246,12 @@ AudioContext::UnregisterPannerNode(PannerNode* aNode)
mPannerNodes.RemoveElement(aNode);
}
void
AudioContext::UnregisterScriptProcessorNode(ScriptProcessorNode* aNode)
{
mScriptProcessorNodes.RemoveElement(aNode);
}
void
AudioContext::UpdatePannerSource()
{
@ -274,6 +281,25 @@ AudioContext::CurrentTime() const
return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime());
}
void
AudioContext::Shutdown()
{
Suspend();
mDecoder.Shutdown();
// Stop all audio buffer source nodes, to make sure that they release
// their self-references.
for (uint32_t i = 0; i < mAudioBufferSourceNodes.Length(); ++i) {
ErrorResult rv;
mAudioBufferSourceNodes[i]->Stop(0.0, rv);
}
// Stop all script processor nodes, to make sure that they release
// their self-references.
for (uint32_t i = 0; i < mScriptProcessorNodes.Length(); ++i) {
mScriptProcessorNodes[i]->Stop();
}
}
void
AudioContext::Suspend()
{

View File

@ -65,12 +65,7 @@ public:
return mWindow;
}
void Shutdown()
{
Suspend();
mDecoder.Shutdown();
}
void Shutdown();
void Suspend();
void Resume();
@ -156,6 +151,7 @@ public:
MediaStream* DestinationStream() const;
void UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode);
void UnregisterPannerNode(PannerNode* aNode);
void UnregisterScriptProcessorNode(ScriptProcessorNode* aNode);
void UpdatePannerSource();
JSContext* GetJSContext() const;
@ -175,6 +171,7 @@ private:
// to compute the doppler shift. Those are weak pointers.
nsTArray<PannerNode*> mPannerNodes;
nsTArray<AudioBufferSourceNode*> mAudioBufferSourceNodes;
nsTArray<ScriptProcessorNode*> mScriptProcessorNodes;
};
}

View File

@ -202,12 +202,18 @@ AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
}
void
AudioNode::UnbindFromEngine()
AudioNode::DestroyMediaStream()
{
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
MOZ_ASSERT(ns, "How come we don't have a stream here?");
MOZ_ASSERT(ns->Engine()->mNode == this, "Invalid node reference");
ns->Engine()->mNode = nullptr;
if (mStream) {
// Remove the node reference on the engine
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
MOZ_ASSERT(ns, "How come we don't have a stream here?");
MOZ_ASSERT(ns->Engine()->mNode == this, "Invalid node reference");
ns->Engine()->mNode = nullptr;
mStream->Destroy();
mStream = nullptr;
}
}
}

View File

@ -78,16 +78,7 @@ public:
virtual ~AudioNode();
// This should be idempotent (safe to call multiple times).
// This should be called in the destructor of every class that overrides
// this method.
virtual void DestroyMediaStream()
{
if (mStream) {
UnbindFromEngine();
mStream->Destroy();
mStream = nullptr;
}
}
virtual void DestroyMediaStream();
// This method should be overridden to return true in nodes
// which support being hooked up to the Media Stream graph.
@ -114,10 +105,10 @@ public:
return mContext;
}
void Connect(AudioNode& aDestination, uint32_t aOutput,
uint32_t aInput, ErrorResult& aRv);
virtual void Connect(AudioNode& aDestination, uint32_t aOutput,
uint32_t aInput, ErrorResult& aRv);
void Disconnect(uint32_t aOutput, ErrorResult& aRv);
virtual void Disconnect(uint32_t aOutput, ErrorResult& aRv);
// The following two virtual methods must be implemented by each node type
// to provide their number of input and output ports. These numbers are
@ -153,8 +144,6 @@ private:
// This could possibly delete 'this'.
void DisconnectFromGraph();
void UnbindFromEngine();
protected:
static void Callback(AudioNode* aNode) { /* not implemented */ }

View File

@ -109,11 +109,6 @@ BiquadFilterNode::BiquadFilterNode(AudioContext* aContext)
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
}
BiquadFilterNode::~BiquadFilterNode()
{
DestroyMediaStream();
}
JSObject*
BiquadFilterNode::WrapObject(JSContext* aCx, JSObject* aScope)
{

View File

@ -33,7 +33,6 @@ class BiquadFilterNode : public AudioNode
{
public:
explicit BiquadFilterNode(AudioContext* aContext);
~BiquadFilterNode();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BiquadFilterNode, AudioNode)

View File

@ -10,6 +10,11 @@
#include "AudioNodeStream.h"
#include "AudioDestinationNode.h"
#include "WebAudioUtils.h"
#include "blink/DynamicsCompressor.h"
using WebCore::DynamicsCompressor;
const unsigned DEFAULT_NUMBER_OF_CHANNELS = 2;
namespace mozilla {
namespace dom {
@ -41,9 +46,10 @@ public:
, mThreshold(-24.f)
, mKnee(30.f)
, mRatio(12.f)
, mReduction(0.f)
, mAttack(0.003f)
, mRelease(0.25f)
, mCompressor(new DynamicsCompressor(aNode->Context()->SampleRate(),
DEFAULT_NUMBER_OF_CHANNELS))
{
}
@ -56,7 +62,6 @@ public:
THRESHOLD,
KNEE,
RATIO,
REDUCTION,
ATTACK,
RELEASE
};
@ -76,10 +81,6 @@ public:
mRatio = aValue;
WebAudioUtils::ConvertAudioParamToTicks(mRatio, mSource, mDestination);
break;
case REDUCTION:
mReduction = aValue;
WebAudioUtils::ConvertAudioParamToTicks(mReduction, mSource, mDestination);
break;
case ATTACK:
mAttack = aValue;
WebAudioUtils::ConvertAudioParamToTicks(mAttack, mSource, mDestination);
@ -98,8 +99,62 @@ public:
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
{
// TODO: do the necessary computation here
*aOutput = aInput;
if (aInput.IsNull()) {
// Just output silence
*aOutput = aInput;
return;
}
TrackTicks pos = aStream->GetCurrentPosition();
mCompressor->setParameterValue(DynamicsCompressor::ParamThreshold,
mThreshold.GetValueAtTime(pos));
mCompressor->setParameterValue(DynamicsCompressor::ParamKnee,
mKnee.GetValueAtTime(pos));
mCompressor->setParameterValue(DynamicsCompressor::ParamRatio,
mRatio.GetValueAtTime(pos));
mCompressor->setParameterValue(DynamicsCompressor::ParamAttack,
mAttack.GetValueAtTime(pos));
mCompressor->setParameterValue(DynamicsCompressor::ParamRelease,
mRelease.GetValueAtTime(pos));
AllocateAudioBlock(DEFAULT_NUMBER_OF_CHANNELS, aOutput);
mCompressor->process(&aInput, aOutput, aInput.GetDuration());
SendReductionParamToMainThread(aStream,
mCompressor->parameterValue(DynamicsCompressor::ParamReduction));
}
private:
void SendReductionParamToMainThread(AudioNodeStream* aStream, float aReduction)
{
MOZ_ASSERT(!NS_IsMainThread());
class Command : public nsRunnable
{
public:
Command(AudioNodeStream* aStream, float aReduction)
: mStream(aStream)
, mReduction(aReduction)
{
}
NS_IMETHODIMP Run()
{
nsRefPtr<DynamicsCompressorNode> node = static_cast<DynamicsCompressorNode*>(mStream->Engine()->Node());
if (node) {
AudioParam* reduction = node->Reduction();
reduction->CancelAllEvents();
reduction->SetValue(mReduction);
}
return NS_OK;
}
private:
nsRefPtr<AudioNodeStream> mStream;
float mReduction;
};
NS_DispatchToMainThread(new Command(aStream, aReduction));
}
private:
@ -108,9 +163,9 @@ private:
AudioParamTimeline mThreshold;
AudioParamTimeline mKnee;
AudioParamTimeline mRatio;
AudioParamTimeline mReduction;
AudioParamTimeline mAttack;
AudioParamTimeline mRelease;
nsAutoPtr<DynamicsCompressor> mCompressor;
};
DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext)
@ -118,20 +173,16 @@ DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext)
, mThreshold(new AudioParam(this, SendThresholdToStream, -24.f))
, mKnee(new AudioParam(this, SendKneeToStream, 30.f))
, mRatio(new AudioParam(this, SendRatioToStream, 12.f))
, mReduction(new AudioParam(this, SendReductionToStream, 0.f))
, mReduction(new AudioParam(this, Callback, 0.f))
, mAttack(new AudioParam(this, SendAttackToStream, 0.003f))
, mRelease(new AudioParam(this, SendReleaseToStream, 0.25f))
{
DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination());
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM,
DEFAULT_NUMBER_OF_CHANNELS);
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
}
DynamicsCompressorNode::~DynamicsCompressorNode()
{
DestroyMediaStream();
}
JSObject*
DynamicsCompressorNode::WrapObject(JSContext* aCx, JSObject* aScope)
{
@ -159,13 +210,6 @@ DynamicsCompressorNode::SendRatioToStream(AudioNode* aNode)
SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::RATIO, *This->mRatio);
}
void
DynamicsCompressorNode::SendReductionToStream(AudioNode* aNode)
{
DynamicsCompressorNode* This = static_cast<DynamicsCompressorNode*>(aNode);
SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::REDUCTION, *This->mReduction);
}
void
DynamicsCompressorNode::SendAttackToStream(AudioNode* aNode)
{

View File

@ -19,7 +19,6 @@ class DynamicsCompressorNode : public AudioNode
{
public:
explicit DynamicsCompressorNode(AudioContext* aContext);
~DynamicsCompressorNode();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DynamicsCompressorNode, AudioNode)
@ -66,7 +65,6 @@ private:
static void SendThresholdToStream(AudioNode* aNode);
static void SendKneeToStream(AudioNode* aNode);
static void SendRatioToStream(AudioNode* aNode);
static void SendReductionToStream(AudioNode* aNode);
static void SendAttackToStream(AudioNode* aNode);
static void SendReleaseToStream(AudioNode* aNode);

View File

@ -103,11 +103,6 @@ GainNode::GainNode(AudioContext* aContext)
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
}
GainNode::~GainNode()
{
DestroyMediaStream();
}
JSObject*
GainNode::WrapObject(JSContext* aCx, JSObject* aScope)
{

View File

@ -19,7 +19,6 @@ class GainNode : public AudioNode
{
public:
explicit GainNode(AudioContext* aContext);
virtual ~GainNode();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GainNode, AudioNode)

View File

@ -184,7 +184,6 @@ PannerNode::~PannerNode()
if (Context()) {
Context()->UnregisterPannerNode(this);
}
DestroyMediaStream();
}
JSObject*

View File

@ -13,13 +13,21 @@
#include "AudioProcessingEvent.h"
#include "WebAudioUtils.h"
#include "mozilla/Mutex.h"
#include "mozilla/unused.h"
#include "mozilla/PodOperations.h"
#include <deque>
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScriptProcessorNode, AudioNode)
if (tmp->Context()) {
tmp->Context()->UnregisterScriptProcessorNode(tmp);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ScriptProcessorNode, AudioNode)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ScriptProcessorNode)
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
@ -304,10 +312,7 @@ private:
// Steal the output buffers
nsRefPtr<ThreadSharedFloatArrayBufferList> output;
if (event->HasOutputBuffer()) {
uint32_t rate, length;
output = event->OutputBuffer()->GetThreadSharedChannelsForRate(cx, &rate, &length);
unused << rate;
unused << length;
output = event->OutputBuffer()->GetThreadSharedChannelsForRate(cx);
}
// Append it to our output buffer queue
@ -363,7 +368,9 @@ ScriptProcessorNode::ScriptProcessorNode(AudioContext* aContext,
ScriptProcessorNode::~ScriptProcessorNode()
{
DestroyMediaStream();
if (Context()) {
Context()->UnregisterScriptProcessorNode(this);
}
}
JSObject*

View File

@ -30,6 +30,7 @@ public:
virtual ~ScriptProcessorNode();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ScriptProcessorNode, AudioNode)
IMPL_EVENT_HANDLER(audioprocess)
@ -40,6 +41,23 @@ public:
return true;
}
virtual void Connect(AudioNode& aDestination, uint32_t aOutput,
uint32_t aInput, ErrorResult& aRv) MOZ_OVERRIDE
{
AudioNode::Connect(aDestination, aOutput, aInput, aRv);
if (!aRv.Failed()) {
mPlayingRef.Take(this);
}
}
virtual void Disconnect(uint32_t aOutput, ErrorResult& aRv) MOZ_OVERRIDE
{
AudioNode::Disconnect(aOutput, aRv);
if (!aRv.Failed()) {
mPlayingRef.Drop(this);
}
}
uint32_t BufferSize() const
{
return mBufferSize;
@ -57,10 +75,16 @@ public:
using nsDOMEventTargetHelper::DispatchTrustedEvent;
void Stop()
{
mPlayingRef.Drop(this);
}
private:
nsAutoPtr<SharedBuffers> mSharedBuffers;
const uint32_t mBufferSize;
const uint32_t mNumberOfOutputChannels;
SelfReference<ScriptProcessorNode> mPlayingRef; // a reference to self while planing
};
}

View File

@ -60,6 +60,14 @@ struct WebAudioUtils {
return aLinearValue ? 20.0f * std::log10(aLinearValue) : aMinDecibels;
}
/**
* Converts a decibel value to a linear value.
*/
static float ConvertDecibelsToLinear(float aDecibels)
{
return std::pow(10.0f, 0.05f * aDecibels);
}
/**
* Converts a decibel to a linear value.
*/
@ -75,6 +83,11 @@ struct WebAudioUtils {
}
}
static double DiscreteTimeConstantForSampleRate(double timeConstant, double sampleRate)
{
return 1.0 - std::exp(-1.0 / (sampleRate * timeConstant));
}
/**
* Convert a stream position into the time coordinate of the destination
* stream.

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2011, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DenormalDisabler_h
#define DenormalDisabler_h
#define _USE_MATH_DEFINES
#include <cmath>
namespace WebCore {
// Deal with denormals. They can very seriously impact performance on x86.
// Define HAVE_DENORMAL if we support flushing denormals to zero.
#if defined(XP_WIN) && defined(_MSC_VER)
#include <float.h>
#define HAVE_DENORMAL
#endif
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
#define HAVE_DENORMAL
#endif
#ifdef HAVE_DENORMAL
class DenormalDisabler {
public:
DenormalDisabler()
: m_savedCSR(0)
{
#if defined(XP_WIN) && defined(_MSC_VER)
// Save the current state, and set mode to flush denormals.
//
// http://stackoverflow.com/questions/637175/possible-bug-in-controlfp-s-may-not-restore-control-word-correctly
_controlfp_s(&m_savedCSR, 0, 0);
unsigned int unused;
_controlfp_s(&unused, _DN_FLUSH, _MCW_DN);
#else
m_savedCSR = getCSR();
setCSR(m_savedCSR | 0x8040);
#endif
}
~DenormalDisabler()
{
#if defined(XP_WIN) && defined(_MSC_VER)
unsigned int unused;
_controlfp_s(&unused, m_savedCSR, _MCW_DN);
#else
setCSR(m_savedCSR);
#endif
}
// This is a nop if we can flush denormals to zero in hardware.
static inline float flushDenormalFloatToZero(float f)
{
#if defined(XP_WIN) && defined(_MSC_VER) && _M_IX86_FP
// For systems using x87 instead of sse, there's no hardware support
// to flush denormals automatically. Hence, we need to flush
// denormals to zero manually.
return (fabs(f) < FLT_MIN) ? 0.0f : f;
#else
return f;
#endif
}
private:
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
inline int getCSR()
{
int result;
asm volatile("stmxcsr %0" : "=m" (result));
return result;
}
inline void setCSR(int a)
{
int temp = a;
asm volatile("ldmxcsr %0" : : "m" (temp));
}
#endif
unsigned int m_savedCSR;
};
#else
// FIXME: add implementations for other architectures and compilers
class DenormalDisabler {
public:
DenormalDisabler() { }
// Assume the worst case that other architectures and compilers
// need to flush denormals to zero manually.
static inline float flushDenormalFloatToZero(float f)
{
return (fabs(f) < FLT_MIN) ? 0.0f : f;
}
};
#endif
} // WebCore
#undef HAVE_DENORMAL
#endif // DenormalDisabler_h

View File

@ -0,0 +1,282 @@
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "DynamicsCompressor.h"
#include "AudioSegment.h"
#include <cmath>
#include "AudioNodeEngine.h"
#include "nsDebug.h"
using mozilla::WEBAUDIO_BLOCK_SIZE;
namespace WebCore {
DynamicsCompressor::DynamicsCompressor(float sampleRate, unsigned numberOfChannels)
: m_numberOfChannels(numberOfChannels)
, m_sampleRate(sampleRate)
, m_compressor(sampleRate, numberOfChannels)
{
// Uninitialized state - for parameter recalculation.
m_lastFilterStageRatio = -1;
m_lastAnchor = -1;
m_lastFilterStageGain = -1;
setNumberOfChannels(numberOfChannels);
initializeParameters();
}
void DynamicsCompressor::setParameterValue(unsigned parameterID, float value)
{
MOZ_ASSERT(parameterID < ParamLast);
if (parameterID < ParamLast)
m_parameters[parameterID] = value;
}
void DynamicsCompressor::initializeParameters()
{
// Initializes compressor to default values.
m_parameters[ParamThreshold] = -24; // dB
m_parameters[ParamKnee] = 30; // dB
m_parameters[ParamRatio] = 12; // unit-less
m_parameters[ParamAttack] = 0.003f; // seconds
m_parameters[ParamRelease] = 0.250f; // seconds
m_parameters[ParamPreDelay] = 0.006f; // seconds
// Release zone values 0 -> 1.
m_parameters[ParamReleaseZone1] = 0.09f;
m_parameters[ParamReleaseZone2] = 0.16f;
m_parameters[ParamReleaseZone3] = 0.42f;
m_parameters[ParamReleaseZone4] = 0.98f;
m_parameters[ParamFilterStageGain] = 4.4f; // dB
m_parameters[ParamFilterStageRatio] = 2;
m_parameters[ParamFilterAnchor] = 15000 / nyquist();
m_parameters[ParamPostGain] = 0; // dB
m_parameters[ParamReduction] = 0; // dB
// Linear crossfade (0 -> 1).
m_parameters[ParamEffectBlend] = 1;
}
float DynamicsCompressor::parameterValue(unsigned parameterID)
{
MOZ_ASSERT(parameterID < ParamLast);
return m_parameters[parameterID];
}
void DynamicsCompressor::setEmphasisStageParameters(unsigned stageIndex, float gain, float normalizedFrequency /* 0 -> 1 */)
{
float gk = 1 - gain / 20;
float f1 = normalizedFrequency * gk;
float f2 = normalizedFrequency / gk;
float r1 = expf(-f1 * M_PI);
float r2 = expf(-f2 * M_PI);
MOZ_ASSERT(m_numberOfChannels == m_preFilterPacks.Length());
for (unsigned i = 0; i < m_numberOfChannels; ++i) {
// Set pre-filter zero and pole to create an emphasis filter.
ZeroPole& preFilter = m_preFilterPacks[i]->filters[stageIndex];
preFilter.setZero(r1);
preFilter.setPole(r2);
// Set post-filter with zero and pole reversed to create the de-emphasis filter.
// If there were no compressor kernel in between, they would cancel each other out (allpass filter).
ZeroPole& postFilter = m_postFilterPacks[i]->filters[stageIndex];
postFilter.setZero(r2);
postFilter.setPole(r1);
}
}
void DynamicsCompressor::setEmphasisParameters(float gain, float anchorFreq, float filterStageRatio)
{
setEmphasisStageParameters(0, gain, anchorFreq);
setEmphasisStageParameters(1, gain, anchorFreq / filterStageRatio);
setEmphasisStageParameters(2, gain, anchorFreq / (filterStageRatio * filterStageRatio));
setEmphasisStageParameters(3, gain, anchorFreq / (filterStageRatio * filterStageRatio * filterStageRatio));
}
void DynamicsCompressor::process(const AudioChunk* sourceChunk, AudioChunk* destinationChunk, unsigned framesToProcess)
{
// Though numberOfChannels is retrived from destinationBus, we still name it numberOfChannels instead of numberOfDestinationChannels.
// It's because we internally match sourceChannels's size to destinationBus by channel up/down mix. Thus we need numberOfChannels
// to do the loop work for both m_sourceChannels and m_destinationChannels.
unsigned numberOfChannels = destinationChunk->mChannelData.Length();
unsigned numberOfSourceChannels = sourceChunk->mChannelData.Length();
MOZ_ASSERT(numberOfChannels == m_numberOfChannels && numberOfSourceChannels);
if (numberOfChannels != m_numberOfChannels || !numberOfSourceChannels) {
destinationChunk->SetNull(WEBAUDIO_BLOCK_SIZE);
return;
}
switch (numberOfChannels) {
case 2: // stereo
m_sourceChannels[0] = static_cast<const float*>(sourceChunk->mChannelData[0]);
if (numberOfSourceChannels > 1)
m_sourceChannels[1] = static_cast<const float*>(sourceChunk->mChannelData[1]);
else
// Simply duplicate mono channel input data to right channel for stereo processing.
m_sourceChannels[1] = m_sourceChannels[0];
break;
default:
// FIXME : support other number of channels.
NS_NOTREACHED("Support other number of channels");
destinationChunk->SetNull(WEBAUDIO_BLOCK_SIZE);
return;
}
for (unsigned i = 0; i < numberOfChannels; ++i)
m_destinationChannels[i] = const_cast<float*>(static_cast<const float*>(
destinationChunk->mChannelData[i]));
float filterStageGain = parameterValue(ParamFilterStageGain);
float filterStageRatio = parameterValue(ParamFilterStageRatio);
float anchor = parameterValue(ParamFilterAnchor);
if (filterStageGain != m_lastFilterStageGain || filterStageRatio != m_lastFilterStageRatio || anchor != m_lastAnchor) {
m_lastFilterStageGain = filterStageGain;
m_lastFilterStageRatio = filterStageRatio;
m_lastAnchor = anchor;
setEmphasisParameters(filterStageGain, anchor, filterStageRatio);
}
// Apply pre-emphasis filter.
// Note that the final three stages are computed in-place in the destination buffer.
for (unsigned i = 0; i < numberOfChannels; ++i) {
const float* sourceData = m_sourceChannels[i];
float* destinationData = m_destinationChannels[i];
ZeroPole* preFilters = m_preFilterPacks[i]->filters;
preFilters[0].process(sourceData, destinationData, framesToProcess);
preFilters[1].process(destinationData, destinationData, framesToProcess);
preFilters[2].process(destinationData, destinationData, framesToProcess);
preFilters[3].process(destinationData, destinationData, framesToProcess);
}
float dbThreshold = parameterValue(ParamThreshold);
float dbKnee = parameterValue(ParamKnee);
float ratio = parameterValue(ParamRatio);
float attackTime = parameterValue(ParamAttack);
float releaseTime = parameterValue(ParamRelease);
float preDelayTime = parameterValue(ParamPreDelay);
// This is effectively a master volume on the compressed signal (pre-blending).
float dbPostGain = parameterValue(ParamPostGain);
// Linear blending value from dry to completely processed (0 -> 1)
// 0 means the signal is completely unprocessed.
// 1 mixes in only the compressed signal.
float effectBlend = parameterValue(ParamEffectBlend);
float releaseZone1 = parameterValue(ParamReleaseZone1);
float releaseZone2 = parameterValue(ParamReleaseZone2);
float releaseZone3 = parameterValue(ParamReleaseZone3);
float releaseZone4 = parameterValue(ParamReleaseZone4);
// Apply compression to the pre-filtered signal.
// The processing is performed in place.
m_compressor.process(m_destinationChannels.get(),
m_destinationChannels.get(),
numberOfChannels,
framesToProcess,
dbThreshold,
dbKnee,
ratio,
attackTime,
releaseTime,
preDelayTime,
dbPostGain,
effectBlend,
releaseZone1,
releaseZone2,
releaseZone3,
releaseZone4
);
// Update the compression amount.
setParameterValue(ParamReduction, m_compressor.meteringGain());
// Apply de-emphasis filter.
for (unsigned i = 0; i < numberOfChannels; ++i) {
float* destinationData = m_destinationChannels[i];
ZeroPole* postFilters = m_postFilterPacks[i]->filters;
postFilters[0].process(destinationData, destinationData, framesToProcess);
postFilters[1].process(destinationData, destinationData, framesToProcess);
postFilters[2].process(destinationData, destinationData, framesToProcess);
postFilters[3].process(destinationData, destinationData, framesToProcess);
}
}
void DynamicsCompressor::reset()
{
m_lastFilterStageRatio = -1; // for recalc
m_lastAnchor = -1;
m_lastFilterStageGain = -1;
for (unsigned channel = 0; channel < m_numberOfChannels; ++channel) {
for (unsigned stageIndex = 0; stageIndex < 4; ++stageIndex) {
m_preFilterPacks[channel]->filters[stageIndex].reset();
m_postFilterPacks[channel]->filters[stageIndex].reset();
}
}
m_compressor.reset();
}
void DynamicsCompressor::setNumberOfChannels(unsigned numberOfChannels)
{
if (m_preFilterPacks.Length() == numberOfChannels)
return;
m_preFilterPacks.Clear();
m_postFilterPacks.Clear();
for (unsigned i = 0; i < numberOfChannels; ++i) {
m_preFilterPacks.AppendElement(new ZeroPoleFilterPack4());
m_postFilterPacks.AppendElement(new ZeroPoleFilterPack4());
}
m_sourceChannels = new const float* [numberOfChannels];
m_destinationChannels = new float* [numberOfChannels];
m_compressor.setNumberOfChannels(numberOfChannels);
m_numberOfChannels = numberOfChannels;
}
} // namespace WebCore

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