mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Merge inbound to mozilla-central r=merge a=merge
This commit is contained in:
commit
ed8dc2034d
@ -1386,7 +1386,7 @@ bool
|
||||
AttrIterator::Next(nsAString& aAttrName, nsAString& aAttrValue)
|
||||
{
|
||||
while (mAttrIdx < mAttrCount) {
|
||||
const nsAttrName* attr = mContent->GetAttrNameAt(mAttrIdx);
|
||||
const nsAttrName* attr = mElement->GetAttrNameAt(mAttrIdx);
|
||||
mAttrIdx++;
|
||||
if (attr->NamespaceEquals(kNameSpaceID_None)) {
|
||||
nsAtom* attrAtom = attr->Atom();
|
||||
@ -1399,17 +1399,17 @@ AttrIterator::Next(nsAString& aAttrName, nsAString& aAttrValue)
|
||||
continue; // No need to handle exposing as obj attribute here
|
||||
|
||||
if ((attrFlags & ATTR_VALTOKEN) &&
|
||||
!nsAccUtils::HasDefinedARIAToken(mContent, attrAtom))
|
||||
!nsAccUtils::HasDefinedARIAToken(mElement, attrAtom))
|
||||
continue; // only expose token based attributes if they are defined
|
||||
|
||||
if ((attrFlags & ATTR_BYPASSOBJ_IF_FALSE) &&
|
||||
mContent->AttrValueIs(kNameSpaceID_None, attrAtom,
|
||||
mElement->AttrValueIs(kNameSpaceID_None, attrAtom,
|
||||
nsGkAtoms::_false, eCaseMatters)) {
|
||||
continue; // only expose token based attribute if value is not 'false'.
|
||||
}
|
||||
|
||||
nsAutoString value;
|
||||
if (mContent->GetAttr(kNameSpaceID_None, attrAtom, value)) {
|
||||
if (mElement->GetAttr(kNameSpaceID_None, attrAtom, value)) {
|
||||
aAttrName.Assign(Substring(attrStr, 5));
|
||||
aAttrValue.Assign(value);
|
||||
return true;
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "nsAtom.h"
|
||||
#include "nsIContent.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
class nsINode;
|
||||
|
||||
@ -288,10 +289,11 @@ bool HasDefinedARIAHidden(nsIContent* aContent);
|
||||
class AttrIterator
|
||||
{
|
||||
public:
|
||||
explicit AttrIterator(nsIContent* aContent) :
|
||||
mContent(aContent), mAttrIdx(0)
|
||||
explicit AttrIterator(nsIContent* aContent)
|
||||
: mElement(aContent->IsElement() ? aContent->AsElement() : nullptr)
|
||||
, mAttrIdx(0)
|
||||
{
|
||||
mAttrCount = mContent->GetAttrCount();
|
||||
mAttrCount = mElement ? mElement->GetAttrCount() : 0;
|
||||
}
|
||||
|
||||
bool Next(nsAString& aAttrName, nsAString& aAttrValue);
|
||||
@ -301,7 +303,7 @@ private:
|
||||
AttrIterator(const AttrIterator&) = delete;
|
||||
AttrIterator& operator= (const AttrIterator&) = delete;
|
||||
|
||||
nsIContent* mContent;
|
||||
dom::Element* mElement;
|
||||
uint32_t mAttrIdx;
|
||||
uint32_t mAttrCount;
|
||||
};
|
||||
|
@ -114,20 +114,22 @@ MustBeAccessible(nsIContent* aContent, DocAccessible* aDocument)
|
||||
if (aContent->GetPrimaryFrame()->IsFocusable())
|
||||
return true;
|
||||
|
||||
uint32_t attrCount = aContent->GetAttrCount();
|
||||
for (uint32_t attrIdx = 0; attrIdx < attrCount; attrIdx++) {
|
||||
const nsAttrName* attr = aContent->GetAttrNameAt(attrIdx);
|
||||
if (attr->NamespaceEquals(kNameSpaceID_None)) {
|
||||
nsAtom* attrAtom = attr->Atom();
|
||||
nsDependentAtomString attrStr(attrAtom);
|
||||
if (!StringBeginsWith(attrStr, NS_LITERAL_STRING("aria-")))
|
||||
continue; // not ARIA
|
||||
if (aContent->IsElement()) {
|
||||
uint32_t attrCount = aContent->AsElement()->GetAttrCount();
|
||||
for (uint32_t attrIdx = 0; attrIdx < attrCount; attrIdx++) {
|
||||
const nsAttrName* attr = aContent->AsElement()->GetAttrNameAt(attrIdx);
|
||||
if (attr->NamespaceEquals(kNameSpaceID_None)) {
|
||||
nsAtom* attrAtom = attr->Atom();
|
||||
nsDependentAtomString attrStr(attrAtom);
|
||||
if (!StringBeginsWith(attrStr, NS_LITERAL_STRING("aria-")))
|
||||
continue; // not ARIA
|
||||
|
||||
// A global state or a property and in case of token defined.
|
||||
uint8_t attrFlags = aria::AttrCharacteristicsFor(attrAtom);
|
||||
if ((attrFlags & ATTR_GLOBAL) && (!(attrFlags & ATTR_VALTOKEN) ||
|
||||
nsAccUtils::HasDefinedARIAToken(aContent, attrAtom))) {
|
||||
return true;
|
||||
// A global state or a property and in case of token defined.
|
||||
uint8_t attrFlags = aria::AttrCharacteristicsFor(attrAtom);
|
||||
if ((attrFlags & ATTR_GLOBAL) && (!(attrFlags & ATTR_VALTOKEN) ||
|
||||
nsAccUtils::HasDefinedARIAToken(aContent, attrAtom))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -465,16 +465,18 @@ ARIAGridAccessible::SetARIASelected(Accessible* aAccessible,
|
||||
if (IsARIARole(nsGkAtoms::table))
|
||||
return NS_OK;
|
||||
|
||||
nsIContent *content = aAccessible->GetContent();
|
||||
nsIContent* content = aAccessible->GetContent();
|
||||
NS_ENSURE_STATE(content);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
if (aIsSelected)
|
||||
rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
|
||||
NS_LITERAL_STRING("true"), aNotify);
|
||||
else
|
||||
rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
|
||||
NS_LITERAL_STRING("false"), aNotify);
|
||||
if (content->IsElement()) {
|
||||
if (aIsSelected)
|
||||
rv = content->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
|
||||
NS_LITERAL_STRING("true"), aNotify);
|
||||
else
|
||||
rv = content->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
|
||||
NS_LITERAL_STRING("false"), aNotify);
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -704,12 +704,14 @@ Accessible::SetSelected(bool aSelect)
|
||||
Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
|
||||
if (select) {
|
||||
if (select->State() & states::MULTISELECTABLE) {
|
||||
if (ARIARoleMap()) {
|
||||
if (mContent->IsElement() && ARIARoleMap()) {
|
||||
if (aSelect) {
|
||||
mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
|
||||
NS_LITERAL_STRING("true"), true);
|
||||
mContent->AsElement()->SetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::aria_selected,
|
||||
NS_LITERAL_STRING("true"), true);
|
||||
} else {
|
||||
mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, true);
|
||||
mContent->AsElement()->UnsetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::aria_selected, true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -1416,8 +1418,12 @@ Accessible::SetCurValue(double aValue)
|
||||
nsAutoString strValue;
|
||||
strValue.AppendFloat(aValue);
|
||||
|
||||
if (!mContent->IsElement())
|
||||
return true;
|
||||
|
||||
return NS_SUCCEEDED(
|
||||
mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true));
|
||||
mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow,
|
||||
strValue, true));
|
||||
}
|
||||
|
||||
role
|
||||
@ -2570,8 +2576,10 @@ Accessible::SetCurrentItem(Accessible* aItem)
|
||||
if (id) {
|
||||
nsAutoString idStr;
|
||||
id->ToString(idStr);
|
||||
mContent->SetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::aria_activedescendant, idStr, true);
|
||||
mContent->AsElement()->SetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::aria_activedescendant,
|
||||
idStr,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,9 +110,13 @@ HTMLSelectListAccessible::CurrentItem()
|
||||
void
|
||||
HTMLSelectListAccessible::SetCurrentItem(Accessible* aItem)
|
||||
{
|
||||
aItem->GetContent()->SetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::selected, NS_LITERAL_STRING("true"),
|
||||
true);
|
||||
if (!aItem->GetContent()->IsElement())
|
||||
return;
|
||||
|
||||
aItem->GetContent()->AsElement()->SetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::selected,
|
||||
NS_LITERAL_STRING("true"),
|
||||
true);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -13,7 +13,8 @@
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// XULSliderAccessible
|
||||
@ -40,7 +41,7 @@ XULSliderAccessible::NativeInteractiveState() const
|
||||
if (NativelyUnavailable())
|
||||
return states::UNAVAILABLE;
|
||||
|
||||
nsIContent* sliderElm = GetSliderElement();
|
||||
dom::Element* sliderElm = GetSliderElement();
|
||||
if (sliderElm) {
|
||||
nsIFrame* frame = sliderElm->GetPrimaryFrame();
|
||||
if (frame && frame->IsFocusable())
|
||||
@ -83,7 +84,7 @@ XULSliderAccessible::DoAction(uint8_t aIndex)
|
||||
if (aIndex != 0)
|
||||
return false;
|
||||
|
||||
nsIContent* sliderElm = GetSliderElement();
|
||||
dom::Element* sliderElm = GetSliderElement();
|
||||
if (sliderElm)
|
||||
DoCommand(sliderElm);
|
||||
|
||||
@ -129,17 +130,17 @@ XULSliderAccessible::SetCurValue(double aValue)
|
||||
|
||||
// Utils
|
||||
|
||||
nsIContent*
|
||||
dom::Element*
|
||||
XULSliderAccessible::GetSliderElement() const
|
||||
{
|
||||
if (!mSliderNode) {
|
||||
if (!mSliderElement) {
|
||||
// XXX: we depend on anonymous content.
|
||||
mSliderNode = mContent->OwnerDoc()->
|
||||
mSliderElement = mContent->OwnerDoc()->
|
||||
GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid,
|
||||
NS_LITERAL_STRING("slider"));
|
||||
}
|
||||
|
||||
return mSliderNode;
|
||||
return mSliderElement;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -163,8 +164,7 @@ XULSliderAccessible::SetSliderAttr(nsAtom* aName, const nsAString& aValue)
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsIContent* sliderElm = GetSliderElement();
|
||||
if (sliderElm)
|
||||
if (dom::Element* sliderElm = GetSliderElement())
|
||||
sliderElm->SetAttr(kNameSpaceID_None, aName, aValue, true);
|
||||
|
||||
return NS_OK;
|
||||
@ -212,3 +212,5 @@ XULThumbAccessible::NativeRole()
|
||||
return roles::INDICATOR;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ protected:
|
||||
/**
|
||||
* Return anonymous slider element.
|
||||
*/
|
||||
nsIContent* GetSliderElement() const;
|
||||
dom::Element* GetSliderElement() const;
|
||||
|
||||
nsresult GetSliderAttr(nsAtom *aName, nsAString& aValue) const;
|
||||
nsresult SetSliderAttr(nsAtom *aName, const nsAString& aValue);
|
||||
@ -52,7 +52,7 @@ protected:
|
||||
bool SetSliderAttr(nsAtom *aName, double aValue);
|
||||
|
||||
private:
|
||||
mutable nsCOMPtr<nsIContent> mSliderNode;
|
||||
mutable RefPtr<dom::Element> mSliderElement;
|
||||
};
|
||||
|
||||
|
||||
|
@ -951,7 +951,7 @@ BrowserPageActions.sendToDevice = {
|
||||
},
|
||||
|
||||
onSubviewPlaced(panelViewNode) {
|
||||
let bodyNode = panelViewNode.firstChild;
|
||||
let bodyNode = panelViewNode.querySelector(".panel-subview-body");
|
||||
for (let node of bodyNode.childNodes) {
|
||||
BrowserPageActions.takeNodeAttributeFromPanel(node, "title");
|
||||
BrowserPageActions.takeNodeAttributeFromPanel(node, "shortcut");
|
||||
@ -970,7 +970,7 @@ BrowserPageActions.sendToDevice = {
|
||||
let url = browser.currentURI.spec;
|
||||
let title = browser.contentTitle;
|
||||
|
||||
let bodyNode = panelViewNode.firstChild;
|
||||
let bodyNode = panelViewNode.querySelector(".panel-subview-body");
|
||||
let panelNode = panelViewNode.closest("panel");
|
||||
|
||||
// This is on top because it also clears the device list between state
|
||||
|
@ -445,6 +445,17 @@ var SidebarUI = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the webpage favicon in sidebar
|
||||
*
|
||||
*/
|
||||
setWebPageIcon(url) {
|
||||
let iconURL = "url(page-icon:" + url + ")";
|
||||
if (this._box.getAttribute("sidebarcommand") == "viewWebPanelsSidebar") {
|
||||
this._icon.style.setProperty("--sidebar-webpage-icon", iconURL);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the sidebar.
|
||||
*
|
||||
|
@ -73,12 +73,10 @@ panelmultiview {
|
||||
}
|
||||
|
||||
panelview {
|
||||
-moz-binding: url("chrome://browser/content/customizableui/panelUI.xml#panelview");
|
||||
-moz-box-orient: vertical;
|
||||
}
|
||||
|
||||
panel[hidden] panelmultiview,
|
||||
panel[hidden] panelview {
|
||||
panel[hidden] panelmultiview {
|
||||
-moz-binding: none;
|
||||
}
|
||||
|
||||
@ -86,8 +84,8 @@ panelview:not([current]):not([in-transition]) {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
panelview[mainview] > .panel-header,
|
||||
panelview:not([title]) > .panel-header {
|
||||
/* Hide the header when a subview is reused as a main view. */
|
||||
panelview[mainview] > .panel-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -1407,6 +1405,16 @@ toolbarpaletteitem[place="palette"][hidden] {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
||||
/* Favicon in Open Bookmark in Sidebar */
|
||||
#sidebar-box[sidebarcommand="viewWebPanelsSidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
|
||||
list-style-image: var(--sidebar-webpage-icon, url(chrome://mozapps/skin/places/defaultFavicon.svg) );
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* WebExtension Sidebars */
|
||||
#sidebar-box[sidebarcommand$="-sidebar-action"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
|
||||
list-style-image: var(--webextension-menuitem-image, inherit);
|
||||
|
@ -5789,6 +5789,7 @@ function asyncOpenWebPanel(event) {
|
||||
if (gWebPanelURI && SidebarUI.browser.contentDocument &&
|
||||
SidebarUI.browser.contentDocument.getElementById("web-panels-browser")) {
|
||||
SidebarUI.browser.contentWindow.loadWebPanel(gWebPanelURI);
|
||||
SidebarUI.setWebPageIcon(gWebPanelURI);
|
||||
}
|
||||
gWebPanelURI = "";
|
||||
SidebarUI.browser.removeEventListener("load", asyncOpenWebPanel, true);
|
||||
|
@ -278,23 +278,23 @@
|
||||
key="viewBookmarksSidebarKb"
|
||||
observes="viewBookmarksSidebar"
|
||||
oncommand="SidebarUI.show('viewBookmarksSidebar');">
|
||||
<observes element="viewBookmarksSidebar" attribute="checked"/>
|
||||
</toolbarbutton>
|
||||
<observes element="viewBookmarksSidebar" attribute="checked"/>
|
||||
</toolbarbutton>
|
||||
<toolbarbutton id="sidebar-switcher-history"
|
||||
label="&historyButton.label;"
|
||||
class="subviewbutton subviewbutton-iconic"
|
||||
key="key_gotoHistory"
|
||||
observes="viewHistorySidebar"
|
||||
oncommand="SidebarUI.show('viewHistorySidebar');">
|
||||
<observes element="viewHistorySidebar" attribute="checked"/>
|
||||
</toolbarbutton>
|
||||
<observes element="viewHistorySidebar" attribute="checked"/>
|
||||
</toolbarbutton>
|
||||
<toolbarbutton id="sidebar-switcher-tabs"
|
||||
label="&syncedTabs.sidebar.label;"
|
||||
class="subviewbutton subviewbutton-iconic"
|
||||
observes="viewTabsSidebar"
|
||||
oncommand="SidebarUI.show('viewTabsSidebar');">
|
||||
<observes element="viewTabsSidebar" attribute="checked"/>
|
||||
</toolbarbutton>
|
||||
<observes element="viewTabsSidebar" attribute="checked"/>
|
||||
</toolbarbutton>
|
||||
<toolbarseparator/>
|
||||
<vbox id="sidebar-extensions"></vbox>
|
||||
<toolbarseparator/>
|
||||
|
@ -1,19 +1,11 @@
|
||||
/*
|
||||
* Make sure that the origin is shown for ContentPermissionPrompt
|
||||
* consumers e.g. geolocation.
|
||||
*/
|
||||
* Make sure that the correct origin is shown for permission prompts.
|
||||
*/
|
||||
|
||||
add_task(async function test_displayURI() {
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "https://test1.example.com/",
|
||||
}, async function(browser) {
|
||||
async function check(contentTask) {
|
||||
await BrowserTestUtils.withNewTab("https://test1.example.com/", async function(browser) {
|
||||
let popupShownPromise = waitForNotificationPanel();
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
content.navigator.geolocation.getCurrentPosition(function(pos) {
|
||||
// Do nothing
|
||||
});
|
||||
});
|
||||
await ContentTask.spawn(browser, null, contentTask);
|
||||
let panel = await popupShownPromise;
|
||||
let notification = panel.children[0];
|
||||
let body = document.getAnonymousElementByAttribute(notification,
|
||||
@ -21,4 +13,63 @@ add_task(async function test_displayURI() {
|
||||
"popup-notification-body");
|
||||
ok(body.innerHTML.includes("example.com"), "Check that at least the eTLD+1 is present in the markup");
|
||||
});
|
||||
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: getRootDirectory(gTestPath),
|
||||
loadUsingSystemPrincipal: true,
|
||||
});
|
||||
channel = channel.QueryInterface(Ci.nsIFileChannel);
|
||||
|
||||
return BrowserTestUtils.withNewTab(channel.file.path, async function(browser) {
|
||||
let popupShownPromise = waitForNotificationPanel();
|
||||
await ContentTask.spawn(browser, null, contentTask);
|
||||
let panel = await popupShownPromise;
|
||||
let notification = panel.children[0];
|
||||
let body = document.getAnonymousElementByAttribute(notification,
|
||||
"class",
|
||||
"popup-notification-body");
|
||||
if (notification.id == "geolocation-notification") {
|
||||
ok(body.innerHTML.includes("local file"), `file:// URIs should be displayed as local file.`);
|
||||
} else {
|
||||
ok(body.innerHTML.includes("Unknown origin"), "file:// URIs should be displayed as unknown origin.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
["media.navigator.permission.fake", true],
|
||||
["media.navigator.permission.force", true],
|
||||
]});
|
||||
});
|
||||
|
||||
add_task(async function test_displayURI_geo() {
|
||||
await check(async function() {
|
||||
content.navigator.geolocation.getCurrentPosition(() => {});
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_displayURI_camera() {
|
||||
await check(async function() {
|
||||
content.navigator.mediaDevices.getUserMedia({video: true, fake: true});
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_displayURI_geo_blob() {
|
||||
await check(async function() {
|
||||
let text = "<script>navigator.geolocation.getCurrentPosition(() => {})</script>";
|
||||
let blob = new Blob([text], {type: "text/html"});
|
||||
let url = content.URL.createObjectURL(blob);
|
||||
content.location.href = url;
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_displayURI_camera_blob() {
|
||||
await check(async function() {
|
||||
let text = "<script>navigator.mediaDevices.getUserMedia({video: true, fake: true})</script>";
|
||||
let blob = new Blob([text], {type: "text/html"});
|
||||
let url = content.URL.createObjectURL(blob);
|
||||
content.location.href = url;
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -32,7 +32,8 @@ function requestDevice(aAudio, aVideo, aShare, aBadDevice = false) {
|
||||
mozMediaSource: aShare,
|
||||
mediaSource: aShare
|
||||
};
|
||||
} else if (useFakeStreams) {
|
||||
}
|
||||
if (useFakeStreams) {
|
||||
opts.fake = true;
|
||||
}
|
||||
|
||||
|
@ -368,9 +368,49 @@ this.PanelMultiView = class {
|
||||
this.panelViews.push(viewNode);
|
||||
}
|
||||
|
||||
goBack(target) {
|
||||
_setHeader(viewNode, titleText) {
|
||||
// If the header already exists, update or remove it as requested.
|
||||
let header = viewNode.firstChild;
|
||||
if (header && header.classList.contains("panel-header")) {
|
||||
if (titleText) {
|
||||
header.querySelector("label").setAttribute("value", titleText);
|
||||
} else {
|
||||
header.remove();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// The header doesn't exist, only create it if needed.
|
||||
if (!titleText) {
|
||||
return;
|
||||
}
|
||||
|
||||
header = this.document.createElement("box");
|
||||
header.classList.add("panel-header");
|
||||
|
||||
let backButton = this.document.createElement("toolbarbutton");
|
||||
backButton.className =
|
||||
"subviewbutton subviewbutton-iconic subviewbutton-back";
|
||||
backButton.setAttribute("closemenu", "none");
|
||||
backButton.setAttribute("tabindex", "0");
|
||||
backButton.setAttribute("tooltip",
|
||||
this.node.getAttribute("data-subviewbutton-tooltip"));
|
||||
backButton.addEventListener("command", () => {
|
||||
// The panelmultiview element may change if the view is reused.
|
||||
viewNode.panelMultiView.goBack();
|
||||
backButton.blur();
|
||||
});
|
||||
|
||||
let label = this.document.createElement("label");
|
||||
label.setAttribute("value", titleText);
|
||||
|
||||
header.append(backButton, label);
|
||||
viewNode.prepend(header);
|
||||
}
|
||||
|
||||
goBack() {
|
||||
let [current, previous] = this.panelViews.back();
|
||||
return this.showSubView(current, target, previous);
|
||||
return this.showSubView(current, null, previous);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -454,6 +494,10 @@ this.PanelMultiView = class {
|
||||
this._placeSubView(viewNode);
|
||||
}
|
||||
|
||||
viewNode.panelMultiView = this.node;
|
||||
this._setHeader(viewNode, viewNode.getAttribute("title") ||
|
||||
(aAnchor && aAnchor.getAttribute("label")));
|
||||
|
||||
let reverse = !!aPreviousView;
|
||||
let previousViewNode = aPreviousView || this._currentSubView;
|
||||
// If the panelview to show is the same as the previous one, the 'ViewShowing'
|
||||
@ -488,10 +532,7 @@ this.PanelMultiView = class {
|
||||
else
|
||||
viewNode.removeAttribute("mainview");
|
||||
|
||||
// Make sure that new panels always have a title set.
|
||||
if (aAnchor) {
|
||||
if (!viewNode.hasAttribute("title"))
|
||||
viewNode.setAttribute("title", aAnchor.getAttribute("label"));
|
||||
viewNode.classList.add("PanelUI-subView");
|
||||
}
|
||||
if (!isMainView && this._mainViewWidth)
|
||||
@ -599,8 +640,8 @@ this.PanelMultiView = class {
|
||||
// aren't enumerable.
|
||||
let {height, width} = previousRect;
|
||||
viewRect = Object.assign({height, width}, viewNode.customRectGetter());
|
||||
let {header} = viewNode;
|
||||
if (header) {
|
||||
let header = viewNode.firstChild;
|
||||
if (header && header.classList.contains("panel-header")) {
|
||||
viewRect.height += this._dwu.getBoundsWithoutFlushing(header).height;
|
||||
}
|
||||
viewNode.setAttribute("in-transition", true);
|
||||
@ -1023,7 +1064,7 @@ this.PanelMultiView = class {
|
||||
if ((dir == "ltr" && keyCode == "ArrowLeft") ||
|
||||
(dir == "rtl" && keyCode == "ArrowRight")) {
|
||||
if (this._canGoBack(view))
|
||||
this.goBack(view.backButton);
|
||||
this.goBack();
|
||||
break;
|
||||
}
|
||||
// If the current button is _not_ one that points to a subview, pressing
|
||||
@ -1089,8 +1130,6 @@ this.PanelMultiView = class {
|
||||
*/
|
||||
_getNavigableElements(view) {
|
||||
let buttons = Array.from(view.querySelectorAll(".subviewbutton:not([disabled])"));
|
||||
if (this._canGoBack(view))
|
||||
buttons.unshift(view.backButton);
|
||||
let dwu = this._dwu;
|
||||
return buttons.filter(button => {
|
||||
let bounds = dwu.getBoundsWithoutFlushing(button);
|
||||
|
@ -17,7 +17,7 @@
|
||||
<resources>
|
||||
<stylesheet src="chrome://browser/content/customizableui/panelUI.css"/>
|
||||
</resources>
|
||||
<content>
|
||||
<content data-subviewbutton-tooltip="&backCmd.label;">
|
||||
<xul:box anonid="viewContainer" class="panel-viewcontainer" xbl:inherits="panelopen,transitioning">
|
||||
<xul:box anonid="viewStack" xbl:inherits="transitioning" class="panel-viewstack">
|
||||
<children includes="panelview"/>
|
||||
@ -38,36 +38,4 @@
|
||||
]]></destructor>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="panelview">
|
||||
<content>
|
||||
<xul:box class="panel-header" anonid="header">
|
||||
<xul:toolbarbutton anonid="back"
|
||||
class="subviewbutton subviewbutton-iconic subviewbutton-back"
|
||||
closemenu="none"
|
||||
tabindex="0"
|
||||
tooltip="&backCmd.label;"
|
||||
oncommand="document.getBindingParent(this).panelMultiView.goBack(); this.blur()"/>
|
||||
<xul:label xbl:inherits="value=title"/>
|
||||
</xul:box>
|
||||
<children/>
|
||||
</content>
|
||||
<implementation>
|
||||
<property name="header"
|
||||
readonly="true"
|
||||
onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'header');"/>
|
||||
<property name="backButton"
|
||||
readonly="true"
|
||||
onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'back');"/>
|
||||
<property name="panelMultiView" readonly="true">
|
||||
<getter><![CDATA[
|
||||
if (!this.parentNode.localName.endsWith("panelmultiview")) {
|
||||
return document.getBindingParent(this.parentNode);
|
||||
}
|
||||
|
||||
return this.parentNode;
|
||||
]]></getter>
|
||||
</property>
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
||||
|
@ -29,7 +29,7 @@ add_task(async function() {
|
||||
is(initialEncoding.getAttribute("label"), "Western", "The western encoding is initially selected");
|
||||
|
||||
// change the encoding
|
||||
let encodings = characterEncodingView.querySelectorAll("toolbarbutton");
|
||||
let encodings = characterEncodingView.querySelectorAll("toolbarbutton:not(.subviewbutton-back)");
|
||||
let newEncoding = encodings[0].hasAttribute("checked") ? encodings[1] : encodings[0];
|
||||
let browserStopPromise = BrowserTestUtils.browserStopped(gBrowser, TEST_PAGE);
|
||||
newEncoding.click();
|
||||
|
@ -148,7 +148,6 @@ const listeners = {
|
||||
"FormValidation:ShowPopup": ["FormValidationHandler"],
|
||||
"FormValidation:HidePopup": ["FormValidationHandler"],
|
||||
"Prompt:Open": ["RemotePrompt"],
|
||||
"Reader:ArticleGet": ["ReaderParent"],
|
||||
"Reader:FaviconRequest": ["ReaderParent"],
|
||||
"Reader:UpdateReaderButton": ["ReaderParent"],
|
||||
// PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN BrowserCLH.js
|
||||
|
@ -216,6 +216,7 @@ function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSec
|
||||
let request = {
|
||||
callID: aCallID,
|
||||
windowID: aWindowID,
|
||||
origin: aContentWindow.origin,
|
||||
documentURI: aContentWindow.document.documentURI,
|
||||
secure: aSecure,
|
||||
requestTypes,
|
||||
|
@ -22,22 +22,6 @@ var ReaderParent = {
|
||||
// Listeners are added in nsBrowserGlue.js
|
||||
receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case "Reader:ArticleGet":
|
||||
this._getArticle(message.data.url, message.target).then((article) => {
|
||||
// Make sure the target browser is still alive before trying to send data back.
|
||||
if (message.target.messageManager) {
|
||||
message.target.messageManager.sendAsyncMessage("Reader:ArticleData", { article });
|
||||
}
|
||||
}, e => {
|
||||
if (e && e.newURL) {
|
||||
// Make sure the target browser is still alive before trying to send data back.
|
||||
if (message.target.messageManager) {
|
||||
message.target.messageManager.sendAsyncMessage("Reader:ArticleData", { newURL: e.newURL });
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case "Reader:FaviconRequest": {
|
||||
if (message.target.messageManager) {
|
||||
let faviconUrl = PlacesUtils.promiseFaviconLinkUrl(message.data.url);
|
||||
|
@ -368,7 +368,13 @@ function prompt(aBrowser, aRequest) {
|
||||
aBrowser.dispatchEvent(new aBrowser.ownerGlobal
|
||||
.CustomEvent("PermissionStateChange"));
|
||||
|
||||
let uri = Services.io.newURI(aRequest.documentURI);
|
||||
let uri;
|
||||
try {
|
||||
// This fails for principals that serialize to "null", e.g. file URIs.
|
||||
uri = Services.io.newURI(aRequest.origin);
|
||||
} catch (e) {
|
||||
uri = Services.io.newURI(aRequest.documentURI);
|
||||
}
|
||||
let host = getHost(uri);
|
||||
let chromeDoc = aBrowser.ownerDocument;
|
||||
let stringBundle = chromeDoc.defaultView.gNavigatorBundle;
|
||||
|
@ -35,14 +35,6 @@ panelmultiview .toolbaritem-combined-buttons > spacer {
|
||||
margin-inline-start: 18px;
|
||||
}
|
||||
|
||||
.subviewbutton[checked="true"] {
|
||||
background-position: top 7px left 4px;
|
||||
}
|
||||
|
||||
.subviewbutton[checked="true"]:-moz-locale-dir(rtl) {
|
||||
background-position: top 7px right 4px;
|
||||
}
|
||||
|
||||
.subviewbutton:not(:-moz-any([image],[targetURI],.cui-withicon, .bookmark-item)) > .menu-iconic-left {
|
||||
display: none;
|
||||
}
|
||||
|
@ -1487,18 +1487,18 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.PanelUI-subView .panel-header > .subviewbutton-back {
|
||||
.panel-header > .subviewbutton-back {
|
||||
-moz-context-properties: fill;
|
||||
fill: var(--arrowpanel-color);
|
||||
list-style-image: url(chrome://browser/skin/arrow-left.svg);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.panel-header > .subviewbutton-back:-moz-locale-dir(rtl) {
|
||||
.subviewbutton-back:-moz-locale-dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.panel-header > .subviewbutton-back > .toolbarbutton-text {
|
||||
.subviewbutton-back > .toolbarbutton-text {
|
||||
/* !important to override .cui-widget-panel toolbarbutton:not([wrap]) > .toolbarbutton-text
|
||||
* selector further down. */
|
||||
display: none !important;
|
||||
|
@ -100,22 +100,43 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
%ifndef XP_MACOSX
|
||||
#sidebarMenu-popup > .subviewbutton[checked="true"] {
|
||||
list-style-image: none;
|
||||
background: url(chrome://browser/skin/check.svg) no-repeat transparent;
|
||||
background-size: 11px 11px;
|
||||
}
|
||||
|
||||
%ifdef XP_MACOSX
|
||||
|
||||
#sidebarMenu-popup > .subviewbutton[checked="true"] {
|
||||
background-position: top 7px left 4px;
|
||||
}
|
||||
|
||||
#sidebarMenu-popup > .subviewbutton[checked="true"]:-moz-locale-dir(rtl) {
|
||||
background-position: top 7px right 4px;
|
||||
}
|
||||
|
||||
%else
|
||||
|
||||
#sidebarMenu-popup > .subviewbutton[checked="true"] {
|
||||
background-position: center left 7px;
|
||||
}
|
||||
|
||||
#sidebarMenu-popup > .subviewbutton[checked="true"]:-moz-locale-dir(rtl) {
|
||||
background-position: center right 7px;
|
||||
}
|
||||
|
||||
/* Allow room for the checkbox drawn as a background image at the start of the toolbarbutton */
|
||||
#sidebarMenu-popup .subviewbutton-iconic > .toolbarbutton-icon {
|
||||
#sidebarMenu-popup > .subviewbutton-iconic > .toolbarbutton-icon {
|
||||
margin-inline-start: 16px;
|
||||
}
|
||||
/* Align items without icons to the start of the icons: */
|
||||
#sidebarMenu-popup .subviewbutton:not(.subviewbutton-iconic) > .toolbarbutton-text {
|
||||
#sidebarMenu-popup > .subviewbutton:not(.subviewbutton-iconic) > .toolbarbutton-text {
|
||||
padding-inline-start: 16px;
|
||||
}
|
||||
|
||||
%endif
|
||||
|
||||
|
||||
#sidebar-box[sidebarcommand="viewWebPanelsSidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
|
||||
list-style-image: url(chrome://mozapps/skin/places/defaultFavicon.svg);
|
||||
}
|
||||
|
||||
#sidebar-switcher-bookmarks > .toolbarbutton-icon,
|
||||
#sidebar-box[sidebarcommand="viewBookmarksSidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
|
||||
list-style-image: url(chrome://browser/skin/bookmark.svg);
|
||||
|
Binary file not shown.
Binary file not shown.
@ -292,3 +292,6 @@ https://mochitest.youtube.com:443
|
||||
# Hosts for stylo blocklist tests
|
||||
http://stylo-blocklist.com:80 privileged
|
||||
http://test.stylo-blocklist.com:80 privileged
|
||||
|
||||
# Host for U2F localhost tests
|
||||
https://localhost:443
|
||||
|
@ -205,11 +205,11 @@ body {
|
||||
}
|
||||
|
||||
.cm-s-mozilla.CodeMirror-focused .CodeMirror-selected { /* selected text (focused) */
|
||||
background: rgb(77, 86, 103);
|
||||
background: var(--theme-selection-background-hover);
|
||||
}
|
||||
|
||||
.cm-s-mozilla .CodeMirror-selected { /* selected text (unfocused) */
|
||||
background: rgb(77, 86, 103);
|
||||
background: var(--theme-selection-background-hover);
|
||||
}
|
||||
|
||||
.cm-s-mozilla .CodeMirror-activeline-background { /* selected color with alpha */
|
||||
|
@ -1134,7 +1134,6 @@ var SyntaxTreeVisitor = {
|
||||
* type: "TryStatement";
|
||||
* block: BlockStatement;
|
||||
* handler: CatchClause | null;
|
||||
* guardedHandlers: [ CatchClause ];
|
||||
* finalizer: BlockStatement | null;
|
||||
* }
|
||||
*/
|
||||
@ -1156,9 +1155,6 @@ var SyntaxTreeVisitor = {
|
||||
if (node.handler) {
|
||||
this[node.handler.type](node.handler, node, callbacks);
|
||||
}
|
||||
for (let guardedHandler of node.guardedHandlers) {
|
||||
this[guardedHandler.type](guardedHandler, node, callbacks);
|
||||
}
|
||||
if (node.finalizer) {
|
||||
this[node.finalizer.type](node.finalizer, node, callbacks);
|
||||
}
|
||||
|
@ -10531,6 +10531,9 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
||||
(aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
|
||||
mURIResultedInDocument = false; // reset the clock...
|
||||
|
||||
// Note that there is code that relies on this check to stop us entering the
|
||||
// `doShortCircuitedLoad` block below for certain load types. (For example,
|
||||
// reftest-content.js uses LOAD_FLAGS_BYPASS_CACHE for this purpose.)
|
||||
if (aLoadType == LOAD_NORMAL ||
|
||||
aLoadType == LOAD_STOP_CONTENT ||
|
||||
LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
|
||||
|
@ -69,33 +69,6 @@ public:
|
||||
|
||||
virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
// nsIContent
|
||||
using nsIContent::SetAttr;
|
||||
virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||
nsAtom* aPrefix, const nsAString& aValue,
|
||||
nsIPrincipal* aSubjectPrincipal,
|
||||
bool aNotify) override
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsAtom* aAttribute,
|
||||
bool aNotify) override
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
virtual const nsAttrName* GetAttrNameAt(uint32_t aIndex) const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
virtual BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const override
|
||||
{
|
||||
return BorrowedAttrInfo(nullptr, nullptr);
|
||||
}
|
||||
virtual uint32_t GetAttrCount() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool IsNodeOfType(uint32_t aFlags) const override;
|
||||
|
||||
virtual nsIDOMNode* AsDOMNode() override { return this; }
|
||||
|
@ -430,6 +430,11 @@ public:
|
||||
virtual nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute,
|
||||
int32_t aModType) const;
|
||||
|
||||
NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
inline Directionality GetDirectionality() const {
|
||||
if (HasFlag(NODE_HAS_DIRECTION_RTL)) {
|
||||
return eDir_RTL;
|
||||
@ -697,8 +702,6 @@ public:
|
||||
already_AddRefed<mozilla::dom::NodeInfo>
|
||||
GetExistingAttrNameFromQName(const nsAString& aStr) const;
|
||||
|
||||
using nsIContent::SetAttr;
|
||||
|
||||
/**
|
||||
* Helper for SetAttr/SetParsedAttr. This method will return true if aNotify
|
||||
* is true or there are mutation listeners that must be triggered, the
|
||||
@ -758,9 +761,6 @@ public:
|
||||
*/
|
||||
nsresult SetSingleClassFromParser(nsAtom* aSingleClassName);
|
||||
|
||||
virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix,
|
||||
const nsAString& aValue, nsIPrincipal* aSubjectPrincipal,
|
||||
bool aNotify) override;
|
||||
// aParsedValue receives the old value of the attribute. That's useful if
|
||||
// either the input or output value of aParsedValue is StoresOwnData.
|
||||
nsresult SetParsedAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix,
|
||||
@ -777,19 +777,97 @@ public:
|
||||
inline bool AttrValueIs(int32_t aNameSpaceID, nsAtom* aName,
|
||||
nsAtom* aValue,
|
||||
nsCaseTreatment aCaseSensitive) const;
|
||||
virtual int32_t FindAttrValueIn(int32_t aNameSpaceID,
|
||||
nsAtom* aName,
|
||||
AttrValuesArray* aValues,
|
||||
nsCaseTreatment aCaseSensitive) const override;
|
||||
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsAtom* aAttribute,
|
||||
bool aNotify) override;
|
||||
int32_t FindAttrValueIn(int32_t aNameSpaceID,
|
||||
nsAtom* aName,
|
||||
AttrValuesArray* aValues,
|
||||
nsCaseTreatment aCaseSensitive) const override;
|
||||
|
||||
virtual const nsAttrName* GetAttrNameAt(uint32_t aIndex) const final override
|
||||
/**
|
||||
* Set attribute values. All attribute values are assumed to have a
|
||||
* canonical string representation that can be used for these
|
||||
* methods. The SetAttr method is assumed to perform a translation
|
||||
* of the canonical form into the underlying content specific
|
||||
* form.
|
||||
*
|
||||
* @param aNameSpaceID the namespace of the attribute
|
||||
* @param aName the name of the attribute
|
||||
* @param aValue the value to set
|
||||
* @param aNotify specifies how whether or not the document should be
|
||||
* notified of the attribute change.
|
||||
*/
|
||||
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||
const nsAString& aValue, bool aNotify)
|
||||
{
|
||||
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
|
||||
}
|
||||
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix,
|
||||
const nsAString& aValue, bool aNotify)
|
||||
{
|
||||
return SetAttr(aNameSpaceID, aName, aPrefix, aValue, nullptr, aNotify);
|
||||
}
|
||||
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAString& aValue,
|
||||
nsIPrincipal* aTriggeringPrincipal, bool aNotify)
|
||||
{
|
||||
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aTriggeringPrincipal, aNotify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attribute values. All attribute values are assumed to have a
|
||||
* canonical String representation that can be used for these
|
||||
* methods. The SetAttr method is assumed to perform a translation
|
||||
* of the canonical form into the underlying content specific
|
||||
* form.
|
||||
*
|
||||
* @param aNameSpaceID the namespace of the attribute
|
||||
* @param aName the name of the attribute
|
||||
* @param aPrefix the prefix of the attribute
|
||||
* @param aValue the value to set
|
||||
* @param aMaybeScriptedPrincipal the principal of the scripted caller responsible
|
||||
* for setting the attribute, or null if no scripted caller can be
|
||||
* determined. A null value here does not guarantee that there is no
|
||||
* scripted caller, but a non-null value does guarantee that a scripted
|
||||
* caller with the given principal is directly responsible for the
|
||||
* attribute change.
|
||||
* @param aNotify specifies how whether or not the document should be
|
||||
* notified of the attribute change.
|
||||
*/
|
||||
virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||
nsAtom* aPrefix, const nsAString& aValue,
|
||||
nsIPrincipal* aMaybeScriptedPrincipal,
|
||||
bool aNotify);
|
||||
|
||||
/**
|
||||
* Remove an attribute so that it is no longer explicitly specified.
|
||||
*
|
||||
* @param aNameSpaceID the namespace id of the attribute
|
||||
* @param aAttr the name of the attribute to unset
|
||||
* @param aNotify specifies whether or not the document should be
|
||||
* notified of the attribute change
|
||||
*/
|
||||
virtual nsresult UnsetAttr(int32_t aNameSpaceID,
|
||||
nsAtom* aAttribute,
|
||||
bool aNotify);
|
||||
|
||||
/**
|
||||
* Get the namespace / name / prefix of a given attribute.
|
||||
*
|
||||
* @param aIndex the index of the attribute name
|
||||
* @returns The name at the given index, or null if the index is
|
||||
* out-of-bounds.
|
||||
* @note The document returned by NodeInfo()->GetDocument() (if one is
|
||||
* present) is *not* necessarily the owner document of the element.
|
||||
* @note The pointer returned by this function is only valid until the
|
||||
* next call of either GetAttrNameAt or SetAttr on the element.
|
||||
*/
|
||||
const nsAttrName* GetAttrNameAt(uint32_t aIndex) const
|
||||
{
|
||||
return mAttrsAndChildren.GetSafeAttrNameAt(aIndex);
|
||||
}
|
||||
|
||||
virtual BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const final override
|
||||
/**
|
||||
* Gets the attribute info (name and value) for this element at a given index.
|
||||
*/
|
||||
BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const
|
||||
{
|
||||
if (aIndex >= mAttrsAndChildren.AttrCount()) {
|
||||
return BorrowedAttrInfo(nullptr, nullptr);
|
||||
@ -798,7 +876,12 @@ public:
|
||||
return mAttrsAndChildren.AttrInfoAt(aIndex);
|
||||
}
|
||||
|
||||
virtual uint32_t GetAttrCount() const final override
|
||||
/**
|
||||
* Get the number of all specified attributes.
|
||||
*
|
||||
* @return the number of attributes
|
||||
*/
|
||||
uint32_t GetAttrCount() const
|
||||
{
|
||||
return mAttrsAndChildren.AttrCount();
|
||||
}
|
||||
@ -1466,7 +1549,7 @@ public:
|
||||
|
||||
void SetAttr(nsAtom* aAttr, const nsAString& aValue, nsIPrincipal& aTriggeringPrincipal, ErrorResult& aError)
|
||||
{
|
||||
aError = nsIContent::SetAttr(kNameSpaceID_None, aAttr, aValue, &aTriggeringPrincipal, true);
|
||||
aError = SetAttr(kNameSpaceID_None, aAttr, aValue, &aTriggeringPrincipal, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -391,7 +391,8 @@ nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
|
||||
// declaration that declares aNamespacePrefix.
|
||||
const nsIContent* content = this;
|
||||
do {
|
||||
if (content->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
|
||||
if (content->IsElement() &&
|
||||
content->AsElement()->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
|
||||
return NS_OK;
|
||||
} while ((content = content->GetParent()));
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -401,11 +402,14 @@ nsAtom*
|
||||
nsIContent::GetLang() const
|
||||
{
|
||||
for (const auto* content = this; content; content = content->GetParent()) {
|
||||
if (!content->GetAttrCount() || !content->IsElement()) {
|
||||
if (!content->IsElement()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* element = content->AsElement();
|
||||
if (!element->GetAttrCount()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// xml:lang has precedence over lang on HTML elements (see
|
||||
// XHTML1 section C.7).
|
||||
@ -1179,12 +1183,6 @@ nsIContent::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse)
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FragmentOrElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
FragmentOrElement::IsLink(nsIURI** aURI) const
|
||||
{
|
||||
@ -2241,8 +2239,8 @@ FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst,
|
||||
const nsAttrValue* value = mAttrsAndChildren.AttrAt(i);
|
||||
nsAutoString valStr;
|
||||
value->ToString(valStr);
|
||||
rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
|
||||
name->GetPrefix(), valStr, false);
|
||||
rv = aDst->AsElement()->SetAttr(name->NamespaceID(), name->LocalName(),
|
||||
name->GetPrefix(), valStr, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
@ -165,8 +165,6 @@ public:
|
||||
virtual void DestroyContent() override;
|
||||
virtual void SaveSubtreeState() override;
|
||||
|
||||
NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override;
|
||||
|
||||
nsIHTMLCollection* Children();
|
||||
uint32_t ChildElementCount()
|
||||
{
|
||||
|
@ -63,7 +63,7 @@ NS_NewXULElement(mozilla::dom::Element** aResult,
|
||||
mozilla::dom::FromParser aFromParser);
|
||||
|
||||
void
|
||||
NS_TrustedNewXULElement(nsIContent** aResult,
|
||||
NS_TrustedNewXULElement(mozilla::dom::Element** aResult,
|
||||
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
|
||||
#endif
|
||||
|
||||
|
@ -5058,13 +5058,13 @@ nsContentUtils::CreateContextualFragment(nsINode* aContextNode,
|
||||
tagName = content->NodeInfo()->QualifiedName();
|
||||
|
||||
// see if we need to add xmlns declarations
|
||||
uint32_t count = content->GetAttrCount();
|
||||
uint32_t count = content->AsElement()->GetAttrCount();
|
||||
bool setDefaultNamespace = false;
|
||||
if (count > 0) {
|
||||
uint32_t index;
|
||||
|
||||
for (index = 0; index < count; index++) {
|
||||
const BorrowedAttrInfo info = content->GetAttrInfoAt(index);
|
||||
const BorrowedAttrInfo info = content->AsElement()->GetAttrInfoAt(index);
|
||||
const nsAttrName* name = info.mName;
|
||||
if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
|
||||
info.mValue->ToString(uriStr);
|
||||
|
@ -421,10 +421,10 @@ nsIdentifierMapEntry::GetImageIdElement()
|
||||
}
|
||||
|
||||
void
|
||||
nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
|
||||
nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<Element>* aElements)
|
||||
{
|
||||
for (size_t i = 0; i < mIdContentList.Length(); ++i) {
|
||||
aElements->AppendObject(mIdContentList[i]);
|
||||
for (Element* element : mIdContentList) {
|
||||
aElements->AppendObject(element);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3776,7 +3776,11 @@ nsDocument::ElementsFromPointHelper(float aX, float aY,
|
||||
// If this helper is called via ElementsFromPoint, we need to make sure
|
||||
// our frame is an element. Otherwise return whatever the top frame is
|
||||
// even if it isn't the top-painted element.
|
||||
if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT)) {
|
||||
// SVG 'text' element's SVGTextFrame doesn't respond to hit-testing, so
|
||||
// if 'node' is a child of such an element then we need to manually defer
|
||||
// to the parent here.
|
||||
if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) &&
|
||||
!nsSVGUtils::IsInSVGTextSubtree(outFrames[i])) {
|
||||
continue;
|
||||
}
|
||||
node = node->GetParent();
|
||||
|
@ -637,40 +637,6 @@ nsGenericDOMDataNode::GetChildren(uint32_t aFilter)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericDOMDataNode::SetAttr(int32_t aNameSpaceID, nsAtom* aAttr,
|
||||
nsAtom* aPrefix, const nsAString& aValue,
|
||||
nsIPrincipal* aContentPrincipal,
|
||||
bool aNotify)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericDOMDataNode::UnsetAttr(int32_t aNameSpaceID, nsAtom* aAttr,
|
||||
bool aNotify)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsAttrName*
|
||||
nsGenericDOMDataNode::GetAttrNameAt(uint32_t aIndex) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BorrowedAttrInfo
|
||||
nsGenericDOMDataNode::GetAttrInfoAt(uint32_t aIndex) const
|
||||
{
|
||||
return BorrowedAttrInfo(nullptr, nullptr);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsGenericDOMDataNode::GetAttrCount() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsGenericDOMDataNode::GetChildCount() const
|
||||
{
|
||||
@ -683,6 +649,7 @@ nsGenericDOMDataNode::GetChildAt(uint32_t aIndex) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
nsGenericDOMDataNode::IndexOf(const nsINode* aPossibleChild) const
|
||||
{
|
||||
@ -1123,26 +1090,6 @@ nsGenericDOMDataNode::GetCurrentValueAtom()
|
||||
return NS_Atomize(val);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGenericDOMDataNode::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
nsGenericDOMDataNode::IsAttributeMapped(const nsAtom* aAttribute) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nsChangeHint
|
||||
nsGenericDOMDataNode::GetAttributeChangeHint(const nsAtom* aAttribute,
|
||||
int32_t aModType) const
|
||||
{
|
||||
NS_NOTREACHED("Shouldn't be calling this!");
|
||||
return nsChangeHint(0);
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericDOMDataNode::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
|
||||
size_t* aNodeSize) const
|
||||
|
@ -133,17 +133,6 @@ public:
|
||||
|
||||
virtual already_AddRefed<nsINodeList> GetChildren(uint32_t aFilter) override;
|
||||
|
||||
|
||||
using nsIContent::SetAttr;
|
||||
virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aAttribute,
|
||||
nsAtom* aPrefix, const nsAString& aValue,
|
||||
nsIPrincipal* aSubjectPrincipal,
|
||||
bool aNotify) override;
|
||||
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsAtom* aAttribute,
|
||||
bool aNotify) override;
|
||||
virtual const nsAttrName* GetAttrNameAt(uint32_t aIndex) const override;
|
||||
virtual mozilla::dom::BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const override;
|
||||
virtual uint32_t GetAttrCount() const override;
|
||||
virtual const nsTextFragment *GetText() override;
|
||||
virtual uint32_t TextLength() const override;
|
||||
virtual nsresult SetText(const char16_t* aBuffer, uint32_t aLength,
|
||||
@ -184,11 +173,6 @@ public:
|
||||
virtual bool IsNodeOfType(uint32_t aFlags) const override;
|
||||
virtual bool IsLink(nsIURI** aURI) const override;
|
||||
|
||||
NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override;
|
||||
NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const;
|
||||
virtual nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute,
|
||||
int32_t aModType) const;
|
||||
|
||||
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
|
||||
bool aPreallocateChildren) const override
|
||||
{
|
||||
|
@ -64,15 +64,15 @@ nsHTMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument,
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent,
|
||||
nsIContent *aOriginalElement,
|
||||
nsHTMLContentSerializer::SerializeHTMLAttributes(Element* aElement,
|
||||
Element* aOriginalElement,
|
||||
nsAString& aTagPrefix,
|
||||
const nsAString& aTagNamespaceURI,
|
||||
nsAtom* aTagName,
|
||||
int32_t aNamespace,
|
||||
nsAString& aStr)
|
||||
{
|
||||
int32_t count = aContent->GetAttrCount();
|
||||
int32_t count = aElement->GetAttrCount();
|
||||
if (!count)
|
||||
return true;
|
||||
|
||||
@ -81,7 +81,7 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent,
|
||||
NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
|
||||
|
||||
for (int32_t index = 0; index < count; index++) {
|
||||
const nsAttrName* name = aContent->GetAttrNameAt(index);
|
||||
const nsAttrName* name = aElement->GetAttrNameAt(index);
|
||||
int32_t namespaceID = name->NamespaceID();
|
||||
nsAtom* attrName = name->LocalName();
|
||||
|
||||
@ -91,7 +91,7 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent,
|
||||
StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
|
||||
continue;
|
||||
}
|
||||
aContent->GetAttr(namespaceID, attrName, valueStr);
|
||||
aElement->GetAttr(namespaceID, attrName, valueStr);
|
||||
|
||||
//
|
||||
// Filter out special case of <br type="_moz"> or <br _moz*>,
|
||||
@ -109,7 +109,7 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent,
|
||||
// This is handled separately in SerializeLIValueAttribute()
|
||||
continue;
|
||||
}
|
||||
bool isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
|
||||
bool isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
|
||||
|
||||
if (((attrName == nsGkAtoms::href &&
|
||||
(namespaceID == kNameSpaceID_None ||
|
||||
@ -120,7 +120,7 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent,
|
||||
// Would be nice to handle OBJECT tags, but that gets more complicated
|
||||
// since we have to search the tag list for CODEBASE as well. For now,
|
||||
// just leave them relative.
|
||||
nsCOMPtr<nsIURI> uri = aContent->GetBaseURI();
|
||||
nsCOMPtr<nsIURI> uri = aElement->GetBaseURI();
|
||||
if (uri) {
|
||||
nsAutoString absURI;
|
||||
rv = NS_MakeAbsoluteURI(absURI, valueStr, uri);
|
||||
@ -137,7 +137,7 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent,
|
||||
// If we're serializing a <meta http-equiv="content-type">,
|
||||
// use the proper value, rather than what's in the document.
|
||||
nsAutoString header;
|
||||
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
|
||||
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
|
||||
if (header.LowerCaseEqualsLiteral("content-type")) {
|
||||
valueStr = NS_LITERAL_STRING("text/html; charset=") +
|
||||
NS_ConvertASCIItoUTF16(mCharset);
|
||||
@ -173,22 +173,20 @@ nsHTMLContentSerializer::AppendElementStart(Element* aElement,
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
||||
nsIContent* content = aElement;
|
||||
|
||||
bool forceFormat = false;
|
||||
nsresult rv = NS_OK;
|
||||
if (!CheckElementStart(content, forceFormat, aStr, rv)) {
|
||||
if (!CheckElementStart(aElement, forceFormat, aStr, rv)) {
|
||||
// When we go to AppendElementEnd for this element, we're going to
|
||||
// MaybeLeaveFromPreContent(). So make sure to MaybeEnterInPreContent()
|
||||
// now, so our PreLevel() doesn't get confused.
|
||||
MaybeEnterInPreContent(content);
|
||||
MaybeEnterInPreContent(aElement);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAtom *name = content->NodeInfo()->NameAtom();
|
||||
int32_t ns = content->GetNameSpaceID();
|
||||
nsAtom *name = aElement->NodeInfo()->NameAtom();
|
||||
int32_t ns = aElement->GetNameSpaceID();
|
||||
|
||||
bool lineBreakBeforeOpen = LineBreakBeforeOpen(ns, name);
|
||||
|
||||
@ -224,7 +222,7 @@ nsHTMLContentSerializer::AppendElementStart(Element* aElement,
|
||||
|
||||
NS_ENSURE_TRUE(AppendToString(nsDependentAtomString(name), aStr), NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
MaybeEnterInPreContent(content);
|
||||
MaybeEnterInPreContent(aElement);
|
||||
|
||||
// for block elements, we increase the indentation
|
||||
if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel())
|
||||
@ -264,7 +262,7 @@ nsHTMLContentSerializer::AppendElementStart(Element* aElement,
|
||||
// Even LI passed above have to go through this
|
||||
// for serializing attributes other than "value".
|
||||
nsAutoString dummyPrefix;
|
||||
NS_ENSURE_TRUE(SerializeHTMLAttributes(content,
|
||||
NS_ENSURE_TRUE(SerializeHTMLAttributes(aElement,
|
||||
aOriginalElement,
|
||||
dummyPrefix,
|
||||
EmptyString(),
|
||||
@ -287,21 +285,18 @@ nsHTMLContentSerializer::AppendElementStart(Element* aElement,
|
||||
NS_ENSURE_TRUE(AppendNewLineToString(aStr), NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(AfterElementStart(content, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_ENSURE_TRUE(AfterElementStart(aElement, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLContentSerializer::AppendElementEnd(Element* aElement,
|
||||
nsAString& aStr)
|
||||
nsHTMLContentSerializer::AppendElementEnd(Element* aElement, nsAString& aStr)
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
||||
nsIContent* content = aElement;
|
||||
|
||||
nsAtom *name = content->NodeInfo()->NameAtom();
|
||||
int32_t ns = content->GetNameSpaceID();
|
||||
nsAtom *name = aElement->NodeInfo()->NameAtom();
|
||||
int32_t ns = aElement->GetNameSpaceID();
|
||||
|
||||
if (ns == kNameSpaceID_XHTML &&
|
||||
(name == nsGkAtoms::script ||
|
||||
@ -312,7 +307,7 @@ nsHTMLContentSerializer::AppendElementEnd(Element* aElement,
|
||||
}
|
||||
|
||||
bool forceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
|
||||
content->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
|
||||
aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
|
||||
|
||||
if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
|
||||
DecrIndentation(name);
|
||||
@ -344,7 +339,7 @@ nsHTMLContentSerializer::AppendElementEnd(Element* aElement,
|
||||
if (!isContainer) {
|
||||
// Keep this in sync with the cleanup at the end of this method.
|
||||
MOZ_ASSERT(name != nsGkAtoms::body);
|
||||
MaybeLeaveFromPreContent(content);
|
||||
MaybeLeaveFromPreContent(aElement);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
@ -376,7 +371,7 @@ nsHTMLContentSerializer::AppendElementEnd(Element* aElement,
|
||||
NS_ENSURE_TRUE(AppendToString(kGreaterThan, aStr), NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Keep this cleanup in sync with the IsContainer() early return above.
|
||||
MaybeLeaveFromPreContent(content);
|
||||
MaybeLeaveFromPreContent(aElement);
|
||||
|
||||
if ((mDoFormat || forceFormat)&& !mDoRaw && !PreLevel()
|
||||
&& LineBreakAfterClose(ns, name)) {
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include "nsXHTMLContentSerializer.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsIContent;
|
||||
class nsAtom;
|
||||
|
||||
class nsHTMLContentSerializer final : public nsXHTMLContentSerializer {
|
||||
@ -37,8 +36,8 @@ class nsHTMLContentSerializer final : public nsXHTMLContentSerializer {
|
||||
protected:
|
||||
|
||||
MOZ_MUST_USE
|
||||
virtual bool SerializeHTMLAttributes(nsIContent* aContent,
|
||||
nsIContent *aOriginalElement,
|
||||
virtual bool SerializeHTMLAttributes(mozilla::dom::Element* aContent,
|
||||
mozilla::dom::Element* aOriginalElement,
|
||||
nsAString& aTagPrefix,
|
||||
const nsAString& aTagNamespaceURI,
|
||||
nsAtom* aTagName,
|
||||
|
@ -15,7 +15,6 @@
|
||||
// Forward declarations
|
||||
class nsAtom;
|
||||
class nsIURI;
|
||||
class nsRuleWalker;
|
||||
class nsAttrValue;
|
||||
class nsAttrName;
|
||||
class nsTextFragment;
|
||||
@ -356,60 +355,6 @@ public:
|
||||
mNodeInfo->NameAtom() == nsGkAtoms::mozgeneratedcontentafter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attribute values. All attribute values are assumed to have a
|
||||
* canonical string representation that can be used for these
|
||||
* methods. The SetAttr method is assumed to perform a translation
|
||||
* of the canonical form into the underlying content specific
|
||||
* form.
|
||||
*
|
||||
* @param aNameSpaceID the namespace of the attribute
|
||||
* @param aName the name of the attribute
|
||||
* @param aValue the value to set
|
||||
* @param aNotify specifies how whether or not the document should be
|
||||
* notified of the attribute change.
|
||||
*/
|
||||
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||
const nsAString& aValue, bool aNotify)
|
||||
{
|
||||
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
|
||||
}
|
||||
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix,
|
||||
const nsAString& aValue, bool aNotify)
|
||||
{
|
||||
return SetAttr(aNameSpaceID, aName, aPrefix, aValue, nullptr, aNotify);
|
||||
}
|
||||
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAString& aValue,
|
||||
nsIPrincipal* aTriggeringPrincipal, bool aNotify)
|
||||
{
|
||||
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aTriggeringPrincipal, aNotify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attribute values. All attribute values are assumed to have a
|
||||
* canonical String representation that can be used for these
|
||||
* methods. The SetAttr method is assumed to perform a translation
|
||||
* of the canonical form into the underlying content specific
|
||||
* form.
|
||||
*
|
||||
* @param aNameSpaceID the namespace of the attribute
|
||||
* @param aName the name of the attribute
|
||||
* @param aPrefix the prefix of the attribute
|
||||
* @param aValue the value to set
|
||||
* @param aMaybeScriptedPrincipal the principal of the scripted caller responsible
|
||||
* for setting the attribute, or null if no scripted caller can be
|
||||
* determined. A null value here does not guarantee that there is no
|
||||
* scripted caller, but a non-null value does guarantee that a scripted
|
||||
* caller with the given principal is directly responsible for the
|
||||
* attribute change.
|
||||
* @param aNotify specifies how whether or not the document should be
|
||||
* notified of the attribute change.
|
||||
*/
|
||||
virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||
nsAtom* aPrefix, const nsAString& aValue,
|
||||
nsIPrincipal* aMaybeScriptedPrincipal,
|
||||
bool aNotify) = 0;
|
||||
|
||||
/**
|
||||
* Get the current value of the attribute. This returns a form that is
|
||||
* suitable for passing back into SetAttr.
|
||||
@ -419,6 +364,8 @@ public:
|
||||
* @param aResult the value (may legitimately be the empty string) [OUT]
|
||||
* @returns true if the attribute was set (even when set to empty string)
|
||||
* false when not set.
|
||||
*
|
||||
* FIXME(emilio): Move to Element.
|
||||
*/
|
||||
bool GetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||
nsAString& aResult) const;
|
||||
@ -492,43 +439,6 @@ public:
|
||||
return ATTR_MISSING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an attribute so that it is no longer explicitly specified.
|
||||
*
|
||||
* @param aNameSpaceID the namespace id of the attribute
|
||||
* @param aAttr the name of the attribute to unset
|
||||
* @param aNotify specifies whether or not the document should be
|
||||
* notified of the attribute change
|
||||
*/
|
||||
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsAtom* aAttr,
|
||||
bool aNotify) = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Get the namespace / name / prefix of a given attribute.
|
||||
*
|
||||
* @param aIndex the index of the attribute name
|
||||
* @returns The name at the given index, or null if the index is
|
||||
* out-of-bounds.
|
||||
* @note The document returned by NodeInfo()->GetDocument() (if one is
|
||||
* present) is *not* necessarily the owner document of the element.
|
||||
* @note The pointer returned by this function is only valid until the
|
||||
* next call of either GetAttrNameAt or SetAttr on the element.
|
||||
*/
|
||||
virtual const nsAttrName* GetAttrNameAt(uint32_t aIndex) const = 0;
|
||||
|
||||
/**
|
||||
* Gets the attribute info (name and value) for this content at a given index.
|
||||
*/
|
||||
virtual mozilla::dom::BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const = 0;
|
||||
|
||||
/**
|
||||
* Get the number of all specified attributes.
|
||||
*
|
||||
* @return the number of attributes
|
||||
*/
|
||||
virtual uint32_t GetAttrCount() const = 0;
|
||||
|
||||
/**
|
||||
* Get direct access (but read only) to the text in the text content.
|
||||
* NOTE: For elements this is *not* the concatenation of all text children,
|
||||
@ -925,12 +835,6 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk aRuleWalker over the content style rules (presentational
|
||||
* hint rules) for this content node.
|
||||
*/
|
||||
NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) = 0;
|
||||
|
||||
/**
|
||||
* Should be called when the node can become editable or when it can stop
|
||||
* being editable (for example when its contentEditable attribute changes,
|
||||
|
@ -778,17 +778,22 @@ nsINode::LookupPrefix(const nsAString& aNamespaceURI, nsAString& aPrefix)
|
||||
// return the prefix (i.e. the attribute localName).
|
||||
for (nsIContent* content = element; content;
|
||||
content = content->GetParent()) {
|
||||
uint32_t attrCount = content->GetAttrCount();
|
||||
if (!content->IsElement()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Element* element = content->AsElement();
|
||||
uint32_t attrCount = element->GetAttrCount();
|
||||
|
||||
for (uint32_t i = 0; i < attrCount; ++i) {
|
||||
const nsAttrName* name = content->GetAttrNameAt(i);
|
||||
const nsAttrName* name = element->GetAttrNameAt(i);
|
||||
|
||||
if (name->NamespaceEquals(kNameSpaceID_XMLNS) &&
|
||||
content->AttrValueIs(kNameSpaceID_XMLNS, name->LocalName(),
|
||||
element->AttrValueIs(kNameSpaceID_XMLNS, name->LocalName(),
|
||||
aNamespaceURI, eCaseMatters)) {
|
||||
// If the localName is "xmlns", the prefix we output should be
|
||||
// null.
|
||||
nsAtom *localName = name->LocalName();
|
||||
nsAtom* localName = name->LocalName();
|
||||
|
||||
if (localName != nsGkAtoms::xmlns) {
|
||||
localName->ToString(aPrefix);
|
||||
|
@ -152,7 +152,7 @@ public:
|
||||
/**
|
||||
* Append all the elements with this id to aElements
|
||||
*/
|
||||
void AppendAllIdContent(nsCOMArray<nsIContent>* aElements);
|
||||
void AppendAllIdContent(nsCOMArray<Element>* aElements);
|
||||
/**
|
||||
* This can fire ID change callbacks.
|
||||
* @return true if the content could be added, false if we failed due
|
||||
|
@ -924,14 +924,14 @@ nsObjectLoadingContent::BuildParametersArray()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> content =
|
||||
nsCOMPtr<Element> element =
|
||||
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
|
||||
|
||||
for (uint32_t i = 0; i != content->GetAttrCount(); i += 1) {
|
||||
for (uint32_t i = 0; i != element->GetAttrCount(); i += 1) {
|
||||
MozPluginParameter param;
|
||||
const nsAttrName* attrName = content->GetAttrNameAt(i);
|
||||
const nsAttrName* attrName = element->GetAttrNameAt(i);
|
||||
nsAtom* atom = attrName->LocalName();
|
||||
content->GetAttr(attrName->NamespaceID(), atom, param.mValue);
|
||||
element->GetAttr(attrName->NamespaceID(), atom, param.mValue);
|
||||
atom->ToString(param.mName);
|
||||
mCachedAttributes.AppendElement(param);
|
||||
}
|
||||
@ -958,10 +958,10 @@ nsObjectLoadingContent::BuildParametersArray()
|
||||
// Nav 4.x would simply replace the "data" with "src". Because some plugins correctly
|
||||
// look for "data", lets instead copy the "data" attribute and add another entry
|
||||
// to the bottom of the array if there isn't already a "src" specified.
|
||||
if (content->IsHTMLElement(nsGkAtoms::object) &&
|
||||
!content->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
|
||||
if (element->IsHTMLElement(nsGkAtoms::object) &&
|
||||
!element->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
|
||||
MozPluginParameter param;
|
||||
content->GetAttr(kNameSpaceID_None, nsGkAtoms::data, param.mValue);
|
||||
element->GetAttr(kNameSpaceID_None, nsGkAtoms::data, param.mValue);
|
||||
if (!param.mValue.IsEmpty()) {
|
||||
param.mName = NS_LITERAL_STRING("SRC");
|
||||
mCachedAttributes.AppendElement(param);
|
||||
|
@ -1401,10 +1401,12 @@ nsTreeSanitizer::SanitizeChildren(nsINode* aRoot)
|
||||
int32_t ns = nodeInfo->NamespaceID();
|
||||
|
||||
if (MustPrune(ns, localName, elt)) {
|
||||
RemoveAllAttributes(node);
|
||||
RemoveAllAttributes(elt);
|
||||
nsIContent* descendant = node;
|
||||
while ((descendant = descendant->GetNextNode(node))) {
|
||||
RemoveAllAttributes(descendant);
|
||||
if (descendant->IsElement()) {
|
||||
RemoveAllAttributes(descendant->AsElement());
|
||||
}
|
||||
}
|
||||
nsIContent* next = node->GetNextNonChildNode(aRoot);
|
||||
node->RemoveFromParent();
|
||||
@ -1450,7 +1452,7 @@ nsTreeSanitizer::SanitizeChildren(nsINode* aRoot)
|
||||
continue;
|
||||
}
|
||||
if (MustFlatten(ns, localName)) {
|
||||
RemoveAllAttributes(node);
|
||||
RemoveAllAttributes(elt);
|
||||
nsCOMPtr<nsIContent> next = node->GetNextNode(aRoot);
|
||||
nsCOMPtr<nsIContent> parent = node->GetParent();
|
||||
nsCOMPtr<nsIContent> child; // Must keep the child alive during move
|
||||
@ -1505,7 +1507,7 @@ nsTreeSanitizer::SanitizeChildren(nsINode* aRoot)
|
||||
}
|
||||
|
||||
void
|
||||
nsTreeSanitizer::RemoveAllAttributes(nsIContent* aElement)
|
||||
nsTreeSanitizer::RemoveAllAttributes(Element* aElement)
|
||||
{
|
||||
const nsAttrName* attrName;
|
||||
while ((attrName = aElement->GetAttrNameAt(0))) {
|
||||
|
@ -146,8 +146,8 @@ class MOZ_STACK_CLASS nsTreeSanitizer {
|
||||
* @return true if the attribute was removed and false otherwise
|
||||
*/
|
||||
bool SanitizeURL(mozilla::dom::Element* aElement,
|
||||
int32_t aNamespace,
|
||||
nsAtom* aLocalName);
|
||||
int32_t aNamespace,
|
||||
nsAtom* aLocalName);
|
||||
|
||||
/**
|
||||
* Checks a style rule for the presence of the 'binding' CSS property and
|
||||
@ -178,7 +178,7 @@ class MOZ_STACK_CLASS nsTreeSanitizer {
|
||||
/**
|
||||
* Removes all attributes from an element node.
|
||||
*/
|
||||
void RemoveAllAttributes(nsIContent* aElement);
|
||||
void RemoveAllAttributes(mozilla::dom::Element* aElement);
|
||||
|
||||
/**
|
||||
* The whitelist of HTML elements.
|
||||
|
@ -156,8 +156,8 @@ nsXHTMLContentSerializer::AppendText(nsIContent* aText,
|
||||
}
|
||||
|
||||
bool
|
||||
nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
nsIContent *aOriginalElement,
|
||||
nsXHTMLContentSerializer::SerializeAttributes(Element* aElement,
|
||||
Element* aOriginalElement,
|
||||
nsAString& aTagPrefix,
|
||||
const nsAString& aTagNamespaceURI,
|
||||
nsAtom* aTagName,
|
||||
@ -171,7 +171,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
nsAutoString xmlnsStr;
|
||||
xmlnsStr.AssignLiteral(kXMLNS);
|
||||
|
||||
int32_t contentNamespaceID = aContent->GetNameSpaceID();
|
||||
int32_t contentNamespaceID = aElement->GetNameSpaceID();
|
||||
|
||||
// this method is not called by nsHTMLContentSerializer
|
||||
// so we don't have to check HTML element, just XHTML
|
||||
@ -185,7 +185,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
// Store its start attribute value in olState->startVal.
|
||||
nsAutoString start;
|
||||
int32_t startAttrVal = 0;
|
||||
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::start, start);
|
||||
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::start, start);
|
||||
if (!start.IsEmpty()) {
|
||||
nsresult rv = NS_OK;
|
||||
startAttrVal = start.ToInteger(&rv);
|
||||
@ -204,7 +204,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
mIsFirstChildOfOL = IsFirstChildOfOL(aOriginalElement);
|
||||
if (mIsFirstChildOfOL) {
|
||||
// If OL is parent of this LI, serialize attributes in different manner.
|
||||
NS_ENSURE_TRUE(SerializeLIValueAttribute(aContent, aStr), false);
|
||||
NS_ENSURE_TRUE(SerializeLIValueAttribute(aElement, aStr), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -228,7 +228,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
|
||||
NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
|
||||
|
||||
count = aContent->GetAttrCount();
|
||||
count = aElement->GetAttrCount();
|
||||
|
||||
// Now serialize each of the attributes
|
||||
// XXX Unfortunately we need a namespace manager to get
|
||||
@ -239,7 +239,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
continue;
|
||||
}
|
||||
|
||||
mozilla::dom::BorrowedAttrInfo info = aContent->GetAttrInfoAt(index);
|
||||
mozilla::dom::BorrowedAttrInfo info = aElement->GetAttrInfoAt(index);
|
||||
const nsAttrName* name = info.mName;
|
||||
|
||||
int32_t namespaceID = name->NamespaceID();
|
||||
@ -287,7 +287,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
continue;
|
||||
}
|
||||
|
||||
isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
|
||||
isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
|
||||
|
||||
if (namespaceID == kNameSpaceID_None &&
|
||||
((attrName == nsGkAtoms::href) ||
|
||||
@ -298,7 +298,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
// but that gets more complicated since we have to
|
||||
// search the tag list for CODEBASE as well.
|
||||
// For now, just leave them relative.
|
||||
nsCOMPtr<nsIURI> uri = aContent->GetBaseURI();
|
||||
nsCOMPtr<nsIURI> uri = aElement->GetBaseURI();
|
||||
if (uri) {
|
||||
nsAutoString absURI;
|
||||
rv = NS_MakeAbsoluteURI(absURI, valueStr, uri);
|
||||
@ -314,7 +314,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
// If we're serializing a <meta http-equiv="content-type">,
|
||||
// use the proper value, rather than what's in the document.
|
||||
nsAutoString header;
|
||||
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
|
||||
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
|
||||
if (header.LowerCaseEqualsLiteral("content-type")) {
|
||||
valueStr = NS_LITERAL_STRING("text/html; charset=") +
|
||||
NS_ConvertASCIItoUTF16(mCharset);
|
||||
@ -327,7 +327,7 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
}
|
||||
}
|
||||
else {
|
||||
isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
|
||||
isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS), false);
|
||||
@ -414,8 +414,8 @@ nsXHTMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument,
|
||||
}
|
||||
|
||||
bool
|
||||
nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent,
|
||||
bool & aForceFormat,
|
||||
nsXHTMLContentSerializer::CheckElementStart(Element* aElement,
|
||||
bool& aForceFormat,
|
||||
nsAString& aStr,
|
||||
nsresult& aResult)
|
||||
{
|
||||
@ -425,16 +425,16 @@ nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent,
|
||||
// indicate that this element should be pretty printed
|
||||
// even if we're not in pretty printing mode
|
||||
aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
|
||||
aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
|
||||
aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
|
||||
|
||||
if (aContent->IsHTMLElement(nsGkAtoms::br) &&
|
||||
if (aElement->IsHTMLElement(nsGkAtoms::br) &&
|
||||
(mFlags & nsIDocumentEncoder::OutputNoFormattingInPre) &&
|
||||
PreLevel() > 0) {
|
||||
aResult = AppendNewLineToString(aStr) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aContent->IsHTMLElement(nsGkAtoms::body)) {
|
||||
if (aElement->IsHTMLElement(nsGkAtoms::body)) {
|
||||
++mInBody;
|
||||
}
|
||||
|
||||
@ -834,7 +834,7 @@ nsXHTMLContentSerializer::IsFirstChildOfOL(nsIContent* aElement)
|
||||
}
|
||||
|
||||
bool
|
||||
nsXHTMLContentSerializer::HasNoChildren(nsIContent * aContent) {
|
||||
nsXHTMLContentSerializer::HasNoChildren(nsIContent* aContent) {
|
||||
|
||||
for (nsIContent* child = aContent->GetFirstChild();
|
||||
child;
|
||||
|
@ -48,10 +48,10 @@ class nsXHTMLContentSerializer : public nsXMLContentSerializer {
|
||||
protected:
|
||||
|
||||
|
||||
virtual bool CheckElementStart(nsIContent * aContent,
|
||||
bool & aForceFormat,
|
||||
nsAString& aStr,
|
||||
nsresult& aResult) override;
|
||||
virtual bool CheckElementStart(mozilla::dom::Element* aElement,
|
||||
bool& aForceFormat,
|
||||
nsAString& aStr,
|
||||
nsresult& aResult) override;
|
||||
|
||||
MOZ_MUST_USE
|
||||
virtual bool AfterElementStart(nsIContent* aContent,
|
||||
@ -77,14 +77,14 @@ class nsXHTMLContentSerializer : public nsXMLContentSerializer {
|
||||
virtual void MaybeLeaveFromPreContent(nsIContent* aNode) override;
|
||||
|
||||
MOZ_MUST_USE
|
||||
virtual bool SerializeAttributes(nsIContent* aContent,
|
||||
nsIContent *aOriginalElement,
|
||||
nsAString& aTagPrefix,
|
||||
const nsAString& aTagNamespaceURI,
|
||||
nsAtom* aTagName,
|
||||
nsAString& aStr,
|
||||
uint32_t aSkipAttr,
|
||||
bool aAddNSAttr) override;
|
||||
virtual bool SerializeAttributes(mozilla::dom::Element* aContent,
|
||||
mozilla::dom::Element* aOriginalElement,
|
||||
nsAString& aTagPrefix,
|
||||
const nsAString& aTagNamespaceURI,
|
||||
nsAtom* aTagName,
|
||||
nsAString& aStr,
|
||||
uint32_t aSkipAttr,
|
||||
bool aAddNSAttr) override;
|
||||
|
||||
bool IsFirstChildOfOL(nsIContent* aElement);
|
||||
|
||||
|
@ -712,20 +712,20 @@ nsXMLContentSerializer::SerializeAttr(const nsAString& aPrefix,
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsXMLContentSerializer::ScanNamespaceDeclarations(nsIContent* aContent,
|
||||
nsIContent *aOriginalElement,
|
||||
nsXMLContentSerializer::ScanNamespaceDeclarations(Element* aElement,
|
||||
Element* aOriginalElement,
|
||||
const nsAString& aTagNamespaceURI)
|
||||
{
|
||||
uint32_t index, count;
|
||||
nsAutoString uriStr, valueStr;
|
||||
|
||||
count = aContent->GetAttrCount();
|
||||
count = aElement->GetAttrCount();
|
||||
|
||||
// First scan for namespace declarations, pushing each on the stack
|
||||
uint32_t skipAttr = count;
|
||||
for (index = 0; index < count; index++) {
|
||||
|
||||
const BorrowedAttrInfo info = aContent->GetAttrInfoAt(index);
|
||||
const BorrowedAttrInfo info = aElement->GetAttrInfoAt(index);
|
||||
const nsAttrName* name = info.mName;
|
||||
|
||||
int32_t namespaceID = name->NamespaceID();
|
||||
@ -799,8 +799,8 @@ nsXMLContentSerializer::IsJavaScript(nsIContent * aContent, nsAtom* aAttrNameAto
|
||||
|
||||
|
||||
bool
|
||||
nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
nsIContent *aOriginalElement,
|
||||
nsXMLContentSerializer::SerializeAttributes(Element* aElement,
|
||||
Element* aOriginalElement,
|
||||
nsAString& aTagPrefix,
|
||||
const nsAString& aTagNamespaceURI,
|
||||
nsAtom* aTagName,
|
||||
@ -828,7 +828,7 @@ nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
|
||||
}
|
||||
|
||||
count = aContent->GetAttrCount();
|
||||
count = aElement->GetAttrCount();
|
||||
|
||||
// Now serialize each of the attributes
|
||||
// XXX Unfortunately we need a namespace manager to get
|
||||
@ -838,7 +838,7 @@ nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
continue;
|
||||
}
|
||||
|
||||
const nsAttrName* name = aContent->GetAttrNameAt(index);
|
||||
const nsAttrName* name = aElement->GetAttrNameAt(index);
|
||||
int32_t namespaceID = name->NamespaceID();
|
||||
nsAtom* attrName = name->LocalName();
|
||||
nsAtom* attrPrefix = name->GetPrefix();
|
||||
@ -863,10 +863,10 @@ nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true);
|
||||
}
|
||||
|
||||
aContent->GetAttr(namespaceID, attrName, valueStr);
|
||||
aElement->GetAttr(namespaceID, attrName, valueStr);
|
||||
|
||||
nsDependentAtomString nameStr(attrName);
|
||||
bool isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
|
||||
bool isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
|
||||
|
||||
NS_ENSURE_TRUE(SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS), false);
|
||||
|
||||
@ -888,15 +888,13 @@ nsXMLContentSerializer::AppendElementStart(Element* aElement,
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
||||
nsIContent* content = aElement;
|
||||
|
||||
bool forceFormat = false;
|
||||
nsresult rv = NS_OK;
|
||||
if (!CheckElementStart(content, forceFormat, aStr, rv)) {
|
||||
if (!CheckElementStart(aElement, forceFormat, aStr, rv)) {
|
||||
// When we go to AppendElementEnd for this element, we're going to
|
||||
// MaybeLeaveFromPreContent(). So make sure to MaybeEnterInPreContent()
|
||||
// now, so our PreLevel() doesn't get confused.
|
||||
MaybeEnterInPreContent(content);
|
||||
MaybeEnterInPreContent(aElement);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -907,11 +905,12 @@ nsXMLContentSerializer::AppendElementStart(Element* aElement,
|
||||
aElement->NodeInfo()->GetName(tagLocalName);
|
||||
aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI);
|
||||
|
||||
uint32_t skipAttr = ScanNamespaceDeclarations(content,
|
||||
aOriginalElement, tagNamespaceURI);
|
||||
uint32_t skipAttr =
|
||||
ScanNamespaceDeclarations(aElement, aOriginalElement, tagNamespaceURI);
|
||||
|
||||
nsAtom *name = content->NodeInfo()->NameAtom();
|
||||
bool lineBreakBeforeOpen = LineBreakBeforeOpen(content->GetNameSpaceID(), name);
|
||||
nsAtom *name = aElement->NodeInfo()->NameAtom();
|
||||
bool lineBreakBeforeOpen =
|
||||
LineBreakBeforeOpen(aElement->GetNameSpaceID(), name);
|
||||
|
||||
if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
|
||||
if (mColPos && lineBreakBeforeOpen) {
|
||||
@ -952,25 +951,26 @@ nsXMLContentSerializer::AppendElementStart(Element* aElement,
|
||||
}
|
||||
NS_ENSURE_TRUE(AppendToString(tagLocalName, aStr), NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
MaybeEnterInPreContent(content);
|
||||
MaybeEnterInPreContent(aElement);
|
||||
|
||||
if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
|
||||
NS_ENSURE_TRUE(IncrIndentation(name), NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(SerializeAttributes(content, aOriginalElement, tagPrefix, tagNamespaceURI,
|
||||
name, aStr, skipAttr, addNSAttr),
|
||||
NS_ENSURE_TRUE(SerializeAttributes(aElement, aOriginalElement, tagPrefix,
|
||||
tagNamespaceURI, name, aStr, skipAttr,
|
||||
addNSAttr),
|
||||
NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
NS_ENSURE_TRUE(AppendEndOfElementStart(aElement, aOriginalElement, aStr),
|
||||
NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()
|
||||
&& LineBreakAfterOpen(content->GetNameSpaceID(), name)) {
|
||||
&& LineBreakAfterOpen(aElement->GetNameSpaceID(), name)) {
|
||||
NS_ENSURE_TRUE(AppendNewLineToString(aStr), NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(AfterElementStart(content, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_ENSURE_TRUE(AfterElementStart(aElement, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1145,8 +1145,8 @@ nsXMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument,
|
||||
}
|
||||
|
||||
bool
|
||||
nsXMLContentSerializer::CheckElementStart(nsIContent * aContent,
|
||||
bool & aForceFormat,
|
||||
nsXMLContentSerializer::CheckElementStart(Element*,
|
||||
bool& aForceFormat,
|
||||
nsAString& aStr,
|
||||
nsresult& aResult)
|
||||
{
|
||||
|
@ -209,13 +209,13 @@ class nsXMLContentSerializer : public nsIContentSerializer {
|
||||
*/
|
||||
void GenerateNewPrefix(nsAString& aPrefix);
|
||||
|
||||
uint32_t ScanNamespaceDeclarations(nsIContent* aContent,
|
||||
nsIContent *aOriginalElement,
|
||||
uint32_t ScanNamespaceDeclarations(mozilla::dom::Element* aContent,
|
||||
mozilla::dom::Element* aOriginalElement,
|
||||
const nsAString& aTagNamespaceURI);
|
||||
|
||||
MOZ_MUST_USE
|
||||
virtual bool SerializeAttributes(nsIContent* aContent,
|
||||
nsIContent *aOriginalElement,
|
||||
virtual bool SerializeAttributes(mozilla::dom::Element* aContent,
|
||||
mozilla::dom::Element* aOriginalElement,
|
||||
nsAString& aTagPrefix,
|
||||
const nsAString& aTagNamespaceURI,
|
||||
nsAtom* aTagName,
|
||||
@ -243,10 +243,10 @@ class nsXMLContentSerializer : public nsIContentSerializer {
|
||||
* by setting aForceFormat to true.
|
||||
* @return boolean true if the element can be output
|
||||
*/
|
||||
virtual bool CheckElementStart(nsIContent * aContent,
|
||||
bool & aForceFormat,
|
||||
nsAString& aStr,
|
||||
nsresult& aResult);
|
||||
virtual bool CheckElementStart(Element* aElement,
|
||||
bool & aForceFormat,
|
||||
nsAString& aStr,
|
||||
nsresult& aResult);
|
||||
|
||||
/**
|
||||
* This method is responsible for appending the '>' at the end of the start
|
||||
|
@ -494,7 +494,8 @@ public:
|
||||
NS_IMETHOD
|
||||
GetName(nsAString& aName) override
|
||||
{
|
||||
aName.AssignLiteral("ReleasingTimerHolder");
|
||||
aName.AssignLiteral("ReleasingTimerHolder for blobURL: ");
|
||||
aName.Append(NS_ConvertUTF8toUTF16(mURI));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -4591,11 +4591,18 @@ ContentParent::CommonCreateWindow(PBrowserParent* aThisTab,
|
||||
if (frameLoader) {
|
||||
frameLoader->GetTabParent(getter_AddRefs(aNewTabParent));
|
||||
}
|
||||
} else if (NS_SUCCEEDED(aResult) && !frameLoaderOwner) {
|
||||
// Fall through to the normal window opening code path when there is no
|
||||
// window which we can open a new tab in.
|
||||
openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
|
||||
} else {
|
||||
*aWindowIsNew = false;
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
// If we didn't retarget our window open into a new window, we should return now.
|
||||
if (openLocation != nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
|
||||
return IPC_OK();
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIWindowWatcher> pwwatch =
|
||||
|
@ -187,7 +187,9 @@ struct AudioChunk {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool IsNull() const { return mBuffer == nullptr; }
|
||||
bool IsNull() const {
|
||||
return mBuffer == nullptr;
|
||||
}
|
||||
void SetNull(StreamTime aDuration)
|
||||
{
|
||||
mBuffer = nullptr;
|
||||
@ -333,13 +335,15 @@ public:
|
||||
void ResampleChunks(SpeexResamplerState* aResampler,
|
||||
uint32_t aInRate,
|
||||
uint32_t aOutRate);
|
||||
|
||||
void AppendFrames(already_AddRefed<ThreadSharedObject> aBuffer,
|
||||
const nsTArray<const float*>& aChannelData,
|
||||
int32_t aDuration, const PrincipalHandle& aPrincipalHandle)
|
||||
{
|
||||
AudioChunk* chunk = AppendChunk(aDuration);
|
||||
chunk->mBuffer = aBuffer;
|
||||
|
||||
MOZ_ASSERT(chunk->mBuffer || aChannelData.IsEmpty(), "Appending invalid data ?");
|
||||
|
||||
for (uint32_t channel = 0; channel < aChannelData.Length(); ++channel) {
|
||||
chunk->mChannelData.AppendElement(aChannelData[channel]);
|
||||
}
|
||||
@ -355,6 +359,9 @@ public:
|
||||
{
|
||||
AudioChunk* chunk = AppendChunk(aDuration);
|
||||
chunk->mBuffer = aBuffer;
|
||||
|
||||
MOZ_ASSERT(chunk->mBuffer || aChannelData.IsEmpty(), "Appending invalid data ?");
|
||||
|
||||
for (uint32_t channel = 0; channel < aChannelData.Length(); ++channel) {
|
||||
chunk->mChannelData.AppendElement(aChannelData[channel]);
|
||||
}
|
||||
@ -363,6 +370,7 @@ public:
|
||||
chunk->mTimeStamp = TimeStamp::Now();
|
||||
#endif
|
||||
chunk->mPrincipalHandle = aPrincipalHandle;
|
||||
|
||||
}
|
||||
// Consumes aChunk, and returns a pointer to the persistent copy of aChunk
|
||||
// in the segment.
|
||||
@ -371,6 +379,9 @@ public:
|
||||
AudioChunk* chunk = AppendChunk(aChunk->mDuration);
|
||||
chunk->mBuffer = aChunk->mBuffer.forget();
|
||||
chunk->mChannelData.SwapElements(aChunk->mChannelData);
|
||||
|
||||
MOZ_ASSERT(chunk->mBuffer || aChunk->mChannelData.IsEmpty(), "Appending invalid data ?");
|
||||
|
||||
chunk->mVolume = aChunk->mVolume;
|
||||
chunk->mBufferFormat = aChunk->mBufferFormat;
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
|
@ -16,17 +16,17 @@ inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate,
|
||||
TrackRate aInRate,
|
||||
TrackTicks aTicks)
|
||||
{
|
||||
NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
|
||||
NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
|
||||
NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
|
||||
MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
|
||||
MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
|
||||
MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
|
||||
return (aTicks * aOutRate) / aInRate;
|
||||
}
|
||||
inline TrackTicks RateConvertTicksRoundUp(TrackRate aOutRate,
|
||||
TrackRate aInRate, TrackTicks aTicks)
|
||||
{
|
||||
NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
|
||||
NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
|
||||
NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
|
||||
MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
|
||||
MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
|
||||
MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
|
||||
return (aTicks * aOutRate + aInRate - 1) / aInRate;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ createHTML({
|
||||
var audioContext;
|
||||
var gUMAudioElement;
|
||||
var analyser;
|
||||
runTest(() => getUserMedia({audio: true})
|
||||
runTest(() => getUserMedia({audio: { echoCancellation: false }})
|
||||
.then(stream => {
|
||||
gUMAudioElement = createMediaElement("audio", "gUMAudio");
|
||||
gUMAudioElement.srcObject = stream;
|
||||
|
@ -423,11 +423,12 @@
|
||||
pc.addTransceiver("foo");
|
||||
ok(false, 'addTransceiver("foo") throws');
|
||||
}
|
||||
catch (e if e instanceof TypeError) {
|
||||
ok(true, 'addTransceiver("foo") throws a TypeError');
|
||||
}
|
||||
catch (e) {
|
||||
ok(false, 'addTransceiver("foo") throws a TypeError');
|
||||
if (e instanceof TypeError) {
|
||||
ok(true, 'addTransceiver("foo") throws a TypeError');
|
||||
} else {
|
||||
ok(false, 'addTransceiver("foo") throws a TypeError');
|
||||
}
|
||||
}
|
||||
|
||||
hasProps(pc.getTransceivers(), []);
|
||||
|
@ -17,13 +17,13 @@ class SingleRwFifo;
|
||||
namespace mozilla {
|
||||
|
||||
typedef struct FarEndAudioChunk_ {
|
||||
uint16_t mSamples;
|
||||
size_t mSamples;
|
||||
bool mOverrun;
|
||||
int16_t mData[1]; // variable-length
|
||||
AudioDataValue mData[1]; // variable-length
|
||||
} FarEndAudioChunk;
|
||||
|
||||
// This class is used to packetize and send the mixed audio from an MSG, in
|
||||
// int16, to the AEC module of WebRTC.org.
|
||||
// float, to the AEC module of WebRTC.org.
|
||||
class AudioOutputObserver
|
||||
{
|
||||
public:
|
||||
|
@ -53,8 +53,6 @@ public:
|
||||
static const int DEFAULT_169_VIDEO_WIDTH = 1280;
|
||||
static const int DEFAULT_169_VIDEO_HEIGHT = 720;
|
||||
|
||||
static const int DEFAULT_SAMPLE_RATE = 32000;
|
||||
|
||||
// This allows using whatever rate the graph is using for the
|
||||
// MediaStreamTrack. This is useful for microphone data, we know it's already
|
||||
// at the correct rate for insertion in the MSG.
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "YuvStamper.h"
|
||||
#endif
|
||||
|
||||
#define AUDIO_RATE mozilla::MediaEngine::DEFAULT_SAMPLE_RATE
|
||||
#define DEFAULT_AUDIO_TIMER_MS 10
|
||||
namespace mozilla {
|
||||
|
||||
@ -332,6 +331,7 @@ NS_IMPL_ISUPPORTS0(MediaEngineDefaultAudioSource)
|
||||
MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource()
|
||||
: MediaEngineAudioSource(kReleased)
|
||||
, mLastNotify(0)
|
||||
, mFreq(1000)
|
||||
{}
|
||||
|
||||
MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource()
|
||||
@ -382,10 +382,8 @@ MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConst
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mFreq = aPrefs.mFreq ? aPrefs.mFreq : 1000;
|
||||
mState = kAllocated;
|
||||
// generate sine wave (default 1KHz)
|
||||
mSineGenerator = new SineWaveGenerator(AUDIO_RATE,
|
||||
static_cast<uint32_t>(aPrefs.mFreq ? aPrefs.mFreq : 1000));
|
||||
*aOutHandle = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -409,9 +407,15 @@ MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
if (!mSineGenerator) {
|
||||
// generate sine wave (default 1KHz)
|
||||
mSineGenerator = new SineWaveGenerator(aStream->GraphRate(), mFreq);
|
||||
}
|
||||
|
||||
// AddTrack will take ownership of segment
|
||||
AudioSegment* segment = new AudioSegment();
|
||||
aStream->AddAudioTrack(aID, AUDIO_RATE, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
|
||||
aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
|
||||
|
||||
// Remember TrackID so we can finish later
|
||||
mTrackID = aID;
|
||||
@ -467,7 +471,7 @@ MediaEngineDefaultAudioSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
MOZ_ASSERT(aID == mTrackID);
|
||||
AudioSegment segment;
|
||||
// avoid accumulating rounding errors
|
||||
TrackTicks desired = aSource->TimeToTicksRoundUp(AUDIO_RATE, aDesiredTime);
|
||||
TrackTicks desired = aSource->TimeToTicksRoundUp(aGraph->GraphRate(), aDesiredTime);
|
||||
TrackTicks delta = desired - mLastNotify;
|
||||
mLastNotify += delta;
|
||||
AppendToSegment(segment, delta, aPrincipalHandle);
|
||||
|
@ -188,8 +188,9 @@ protected:
|
||||
TrackID mTrackID;
|
||||
|
||||
TrackTicks mLastNotify; // Accessed in ::Start(), then on NotifyPull (from MSG thread)
|
||||
uint32_t mFreq; // ditto
|
||||
|
||||
// Created on Allocate, then accessed from NotifyPull (MSG thread)
|
||||
// Created on Start, then accessed from NotifyPull (MSG thread)
|
||||
nsAutoPtr<SineWaveGenerator> mSineGenerator;
|
||||
};
|
||||
|
||||
|
@ -107,7 +107,6 @@ void AudioInputCubeb::UpdateDeviceList()
|
||||
|
||||
MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
|
||||
: mMutex("mozilla::MediaEngineWebRTC"),
|
||||
mVoiceEngine(nullptr),
|
||||
mAudioInput(nullptr),
|
||||
mFullDuplex(aPrefs.mFullDuplex),
|
||||
mDelayAgnostic(aPrefs.mDelayAgnostic),
|
||||
@ -280,43 +279,11 @@ MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
JavaVM* jvm = mozilla::jni::GetVM();
|
||||
jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef();
|
||||
|
||||
if (webrtc::VoiceEngine::SetAndroidObjects(jvm, (void*)context) != 0) {
|
||||
LOG(("VoiceEngine:SetAndroidObjects Failed"));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!mVoiceEngine) {
|
||||
mVoiceEngine = webrtc::VoiceEngine::Create();
|
||||
if (!mVoiceEngine) {
|
||||
if (!mAudioInput) {
|
||||
if (!SupportsDuplex()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
|
||||
if (!ptrVoEBase) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always re-init the voice engine, since if we close the last use we
|
||||
// DeInitEngine() and Terminate(), which shuts down Process() - but means
|
||||
// we have to Init() again before using it. Init() when already inited is
|
||||
// just a no-op, so call always.
|
||||
if (ptrVoEBase->Init() < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mAudioInput) {
|
||||
if (SupportsDuplex()) {
|
||||
// The platform_supports_full_duplex.
|
||||
mAudioInput = new mozilla::AudioInputCubeb(mVoiceEngine);
|
||||
} else {
|
||||
mAudioInput = new mozilla::AudioInputWebRTC(mVoiceEngine);
|
||||
}
|
||||
mAudioInput = new mozilla::AudioInputCubeb();
|
||||
}
|
||||
|
||||
int nDevices = 0;
|
||||
@ -344,7 +311,6 @@ MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
|
||||
|
||||
if (uniqueId[0] == '\0') {
|
||||
// Mac and Linux don't set uniqueId!
|
||||
MOZ_ASSERT(sizeof(deviceName) == sizeof(uniqueId)); // total paranoia
|
||||
strcpy(uniqueId, deviceName); // safe given assert and initialization/error-check
|
||||
}
|
||||
|
||||
@ -354,16 +320,7 @@ MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
|
||||
// We've already seen this device, just append.
|
||||
aASources->AppendElement(aSource.get());
|
||||
} else {
|
||||
AudioInput* audioinput = mAudioInput;
|
||||
if (SupportsDuplex()) {
|
||||
// The platform_supports_full_duplex.
|
||||
|
||||
// For cubeb, it has state (the selected ID)
|
||||
// XXX just use the uniqueID for cubeb and support it everywhere, and get rid of this
|
||||
// XXX Small window where the device list/index could change!
|
||||
audioinput = new mozilla::AudioInputCubeb(mVoiceEngine, i);
|
||||
}
|
||||
aSource = new MediaEngineWebRTCMicrophoneSource(mVoiceEngine, audioinput,
|
||||
aSource = new MediaEngineWebRTCMicrophoneSource(new mozilla::AudioInputCubeb(i),
|
||||
i, deviceName, uniqueId,
|
||||
mDelayAgnostic, mExtendedFilter);
|
||||
mAudioSources.Put(uuid, aSource); // Hashtable takes ownership.
|
||||
@ -401,13 +358,6 @@ MediaEngineWebRTC::Shutdown()
|
||||
mVideoSources.Clear();
|
||||
mAudioSources.Clear();
|
||||
|
||||
if (mVoiceEngine) {
|
||||
mVoiceEngine->SetTraceCallback(nullptr);
|
||||
webrtc::VoiceEngine::Delete(mVoiceEngine);
|
||||
}
|
||||
|
||||
mVoiceEngine = nullptr;
|
||||
|
||||
mozilla::camera::Shutdown();
|
||||
AudioInputCubeb::CleanupGlobalData();
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ protected:
|
||||
class AudioInput
|
||||
{
|
||||
public:
|
||||
explicit AudioInput(webrtc::VoiceEngine* aVoiceEngine) : mVoiceEngine(aVoiceEngine) {};
|
||||
AudioInput() = default;
|
||||
// Threadsafe because it's referenced from an MicrophoneSource, which can
|
||||
// had references to it on other threads.
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioInput)
|
||||
@ -160,15 +160,13 @@ public:
|
||||
protected:
|
||||
// Protected destructor, to discourage deletion outside of Release():
|
||||
virtual ~AudioInput() {}
|
||||
|
||||
webrtc::VoiceEngine* mVoiceEngine;
|
||||
};
|
||||
|
||||
class AudioInputCubeb final : public AudioInput
|
||||
{
|
||||
public:
|
||||
explicit AudioInputCubeb(webrtc::VoiceEngine* aVoiceEngine, int aIndex = 0) :
|
||||
AudioInput(aVoiceEngine), mSelectedDevice(aIndex), mInUseCount(0)
|
||||
explicit AudioInputCubeb(int aIndex = 0) :
|
||||
AudioInput(), mSelectedDevice(aIndex), mInUseCount(0)
|
||||
{
|
||||
if (!mDeviceIndexes) {
|
||||
mDeviceIndexes = new nsTArray<int>;
|
||||
@ -314,14 +312,7 @@ public:
|
||||
MOZ_ASSERT(mDevices.count > 0);
|
||||
#endif
|
||||
|
||||
if (mInUseCount == 0) {
|
||||
ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoEXMedia;
|
||||
ptrVoEXMedia = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
|
||||
if (ptrVoEXMedia) {
|
||||
ptrVoEXMedia->SetExternalRecordingStatus(true);
|
||||
}
|
||||
mAnyInUse = true;
|
||||
}
|
||||
mAnyInUse = true;
|
||||
mInUseCount++;
|
||||
// Always tell the stream we're using it for input
|
||||
aStream->OpenAudioInput(mSelectedDevice, aListener);
|
||||
@ -369,77 +360,6 @@ private:
|
||||
static uint32_t sUserChannelCount;
|
||||
};
|
||||
|
||||
class AudioInputWebRTC final : public AudioInput
|
||||
{
|
||||
public:
|
||||
explicit AudioInputWebRTC(webrtc::VoiceEngine* aVoiceEngine) : AudioInput(aVoiceEngine) {}
|
||||
|
||||
int GetNumOfRecordingDevices(int& aDevices)
|
||||
{
|
||||
ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
|
||||
ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
|
||||
if (!ptrVoEBase) {
|
||||
return 1;
|
||||
}
|
||||
aDevices = ptrVoEBase->audio_device_module()->RecordingDevices();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetRecordingDeviceName(int aIndex, char (&aStrNameUTF8)[128],
|
||||
char aStrGuidUTF8[128])
|
||||
{
|
||||
ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
|
||||
ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
|
||||
if (!ptrVoEBase) {
|
||||
return 1;
|
||||
}
|
||||
return ptrVoEBase->audio_device_module()->RecordingDeviceName(aIndex,
|
||||
aStrNameUTF8,
|
||||
aStrGuidUTF8);
|
||||
}
|
||||
|
||||
int GetRecordingDeviceStatus(bool& aIsAvailable)
|
||||
{
|
||||
ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
|
||||
ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
|
||||
if (!ptrVoEBase) {
|
||||
return 1;
|
||||
}
|
||||
return ptrVoEBase->audio_device_module()->RecordingIsAvailable(&aIsAvailable);
|
||||
}
|
||||
|
||||
void GetChannelCount(uint32_t& aChannels)
|
||||
{
|
||||
aChannels = 1; // default to mono
|
||||
}
|
||||
|
||||
int GetMaxAvailableChannels(uint32_t& aChannels)
|
||||
{
|
||||
aChannels = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SetUserChannelCount(uint32_t aChannels)
|
||||
{}
|
||||
|
||||
void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) {}
|
||||
void StopRecording(SourceMediaStream *aStream) {}
|
||||
|
||||
int SetRecordingDevice(int aIndex)
|
||||
{
|
||||
ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
|
||||
ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
|
||||
if (!ptrVoEBase) {
|
||||
return 1;
|
||||
}
|
||||
return ptrVoEBase->audio_device_module()->SetRecordingDevice(aIndex);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Protected destructor, to discourage deletion outside of Release():
|
||||
~AudioInputWebRTC() {}
|
||||
};
|
||||
|
||||
class WebRTCAudioDataListener : public AudioDataListener
|
||||
{
|
||||
protected:
|
||||
@ -490,13 +410,11 @@ private:
|
||||
RefPtr<MediaEngineAudioSource> mAudioSource;
|
||||
};
|
||||
|
||||
class MediaEngineWebRTCMicrophoneSource : public MediaEngineAudioSource,
|
||||
public webrtc::VoEMediaProcess
|
||||
class MediaEngineWebRTCMicrophoneSource : public MediaEngineAudioSource
|
||||
{
|
||||
typedef MediaEngineAudioSource Super;
|
||||
public:
|
||||
MediaEngineWebRTCMicrophoneSource(webrtc::VoiceEngine* aVoiceEnginePtr,
|
||||
mozilla::AudioInput* aAudioInput,
|
||||
MediaEngineWebRTCMicrophoneSource(mozilla::AudioInput* aAudioInput,
|
||||
int aIndex,
|
||||
const char* name,
|
||||
const char* uuid,
|
||||
@ -550,11 +468,6 @@ public:
|
||||
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
|
||||
const nsString& aDeviceId) const override;
|
||||
|
||||
// VoEMediaProcess.
|
||||
virtual void Process(int channel, webrtc::ProcessingTypes type,
|
||||
int16_t audio10ms[], size_t length,
|
||||
int samplingFreq, bool isStereo) override;
|
||||
|
||||
void Shutdown() override;
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
@ -571,20 +484,16 @@ private:
|
||||
const nsString& aDeviceId,
|
||||
const char** aOutBadConstraint) override;
|
||||
|
||||
|
||||
void UpdateAECSettingsIfNeeded(bool aEnable, webrtc::EcModes aMode);
|
||||
void UpdateAGCSettingsIfNeeded(bool aEnable, webrtc::AgcModes aMode);
|
||||
void UpdateNSSettingsIfNeeded(bool aEnable, webrtc::NsModes aMode);
|
||||
|
||||
void SetLastPrefs(const MediaEnginePrefs& aPrefs);
|
||||
|
||||
// These allocate/configure and release the channel
|
||||
bool AllocChannel();
|
||||
void FreeChannel();
|
||||
// These start/stop VoEBase and associated interfaces
|
||||
bool InitEngine();
|
||||
void DeInitEngine();
|
||||
|
||||
// This is true when all processing is disabled, we can skip
|
||||
// packetization, resampling and other processing passes.
|
||||
bool PassThrough() {
|
||||
return mSkipProcessing;
|
||||
}
|
||||
template<typename T>
|
||||
void InsertInGraph(const T* aBuffer,
|
||||
size_t aFrames,
|
||||
@ -596,22 +505,27 @@ private:
|
||||
TrackRate aRate,
|
||||
uint32_t aChannels);
|
||||
|
||||
webrtc::VoiceEngine* mVoiceEngine;
|
||||
|
||||
// This is true when all processing is disabled, we can skip
|
||||
// packetization, resampling and other processing passes.
|
||||
bool PassThrough() {
|
||||
return mSkipProcessing;
|
||||
}
|
||||
void SetPassThrough(bool aPassThrough) {
|
||||
mSkipProcessing = aPassThrough;
|
||||
}
|
||||
|
||||
RefPtr<mozilla::AudioInput> mAudioInput;
|
||||
RefPtr<WebRTCAudioDataListener> mListener;
|
||||
RefPtr<AudioOutputObserver> mAudioOutputObserver;
|
||||
|
||||
// Note: shared across all microphone sources - we don't want to Terminate()
|
||||
// the VoEBase until there are no active captures
|
||||
// Note: shared across all microphone sources
|
||||
static int sChannelsOpen;
|
||||
static ScopedCustomReleasePtr<webrtc::VoEBase> mVoEBase;
|
||||
static ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERender;
|
||||
static ScopedCustomReleasePtr<webrtc::VoENetwork> mVoENetwork;
|
||||
static ScopedCustomReleasePtr<webrtc::VoEAudioProcessing> mVoEProcessing;
|
||||
|
||||
const UniquePtr<webrtc::AudioProcessing> mAudioProcessing;
|
||||
const RefPtr<AudioOutputObserver> mAudioOutputObserver;
|
||||
|
||||
// accessed from the GraphDriver thread except for deletion
|
||||
nsAutoPtr<AudioPacketizer<AudioDataValue, int16_t>> mPacketizer;
|
||||
nsAutoPtr<AudioPacketizer<AudioDataValue, float>> mPacketizer;
|
||||
ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERenderListener;
|
||||
|
||||
// mMonitor protects mSources[] and mPrinicpalIds[] access/changes, and
|
||||
@ -623,7 +537,6 @@ private:
|
||||
nsTArray<PrincipalHandle> mPrincipalHandles; // Maps to mSources.
|
||||
|
||||
int mCapIndex;
|
||||
int mChannel;
|
||||
bool mDelayAgnostic;
|
||||
bool mExtendedFilter;
|
||||
MOZ_INIT_OUTSIDE_CTOR TrackID mTrackID;
|
||||
@ -636,18 +549,18 @@ private:
|
||||
uint64_t mTotalFrames;
|
||||
uint64_t mLastLogFrames;
|
||||
|
||||
NullTransport *mNullTransport;
|
||||
|
||||
nsTArray<int16_t> mInputBuffer;
|
||||
// mSkipProcessing is true if none of the processing passes are enabled,
|
||||
// because of prefs or constraints. This allows simply copying the audio into
|
||||
// the MSG, skipping resampling and the whole webrtc.org code.
|
||||
// This is read and written to only on the MSG thread.
|
||||
bool mSkipProcessing;
|
||||
|
||||
// To only update microphone when needed, we keep track of previous settings.
|
||||
MediaEnginePrefs mLastPrefs;
|
||||
|
||||
AlignedShortBuffer mInputDownmixBuffer;
|
||||
AlignedFloatBuffer mInputBuffer;
|
||||
AlignedFloatBuffer mDeinterleavedBuffer;
|
||||
AlignedAudioBuffer mInputDownmixBuffer;
|
||||
};
|
||||
|
||||
class MediaEngineWebRTC : public MediaEngine
|
||||
@ -676,7 +589,6 @@ private:
|
||||
|
||||
// gUM runnables can e.g. Enumerate from multiple threads
|
||||
Mutex mMutex;
|
||||
webrtc::VoiceEngine* mVoiceEngine;
|
||||
RefPtr<mozilla::AudioInput> mAudioInput;
|
||||
bool mFullDuplex;
|
||||
bool mDelayAgnostic;
|
||||
|
@ -10,19 +10,18 @@
|
||||
#include "mtransport/runnable_utils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "AudioConverter.h"
|
||||
#include "MediaStreamGraphImpl.h"
|
||||
|
||||
// scoped_ptr.h uses FF
|
||||
#ifdef FF
|
||||
#undef FF
|
||||
#endif
|
||||
#include "webrtc/modules/audio_device/opensl/single_rw_fifo.h"
|
||||
#include "webrtc/voice_engine/voice_engine_defines.h"
|
||||
#include "webrtc/modules/audio_processing/include/audio_processing.h"
|
||||
#include "webrtc/common_audio/include/audio_util.h"
|
||||
|
||||
#define CHANNELS 1
|
||||
#define ENCODING "L16"
|
||||
#define DEFAULT_PORT 5555
|
||||
|
||||
#define SAMPLE_RATE(freq) ((freq)*2*8) // bps, 16-bit samples
|
||||
#define SAMPLE_LENGTH(freq) (((freq)*10)/1000)
|
||||
using namespace webrtc;
|
||||
|
||||
// These are restrictions from the webrtc.org code
|
||||
#define MAX_CHANNELS 2
|
||||
@ -53,10 +52,6 @@ NS_IMPL_ISUPPORTS0(MediaEngineWebRTCMicrophoneSource)
|
||||
NS_IMPL_ISUPPORTS0(MediaEngineWebRTCAudioCaptureSource)
|
||||
|
||||
int MediaEngineWebRTCMicrophoneSource::sChannelsOpen = 0;
|
||||
ScopedCustomReleasePtr<webrtc::VoEBase> MediaEngineWebRTCMicrophoneSource::mVoEBase;
|
||||
ScopedCustomReleasePtr<webrtc::VoEExternalMedia> MediaEngineWebRTCMicrophoneSource::mVoERender;
|
||||
ScopedCustomReleasePtr<webrtc::VoENetwork> MediaEngineWebRTCMicrophoneSource::mVoENetwork;
|
||||
ScopedCustomReleasePtr<webrtc::VoEAudioProcessing> MediaEngineWebRTCMicrophoneSource::mVoEProcessing;
|
||||
|
||||
AudioOutputObserver::AudioOutputObserver()
|
||||
: mPlayoutFreq(0)
|
||||
@ -67,7 +62,7 @@ AudioOutputObserver::AudioOutputObserver()
|
||||
, mDownmixBuffer(MAX_SAMPLING_FREQ * MAX_CHANNELS / 100)
|
||||
{
|
||||
// Buffers of 10ms chunks
|
||||
mPlayoutFifo = new webrtc::SingleRwFifo(MAX_AEC_FIFO_DEPTH/10);
|
||||
mPlayoutFifo = new SingleRwFifo(MAX_AEC_FIFO_DEPTH/10);
|
||||
}
|
||||
|
||||
AudioOutputObserver::~AudioOutputObserver()
|
||||
@ -143,7 +138,7 @@ AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aFrame
|
||||
while (aFrames) {
|
||||
if (!mSaved) {
|
||||
mSaved = (FarEndAudioChunk *) moz_xmalloc(sizeof(FarEndAudioChunk) +
|
||||
(mChunkSize * channels - 1)*sizeof(int16_t));
|
||||
(mChunkSize * channels - 1)*sizeof(AudioDataValue));
|
||||
mSaved->mSamples = mChunkSize;
|
||||
mSaved->mOverrun = aOverran;
|
||||
aOverran = false;
|
||||
@ -153,7 +148,7 @@ AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aFrame
|
||||
to_copy = aFrames;
|
||||
}
|
||||
|
||||
int16_t* dest = &(mSaved->mData[mSamplesSaved * channels]);
|
||||
AudioDataValue* dest = &(mSaved->mData[mSamplesSaved * channels]);
|
||||
if (aChannels > MAX_CHANNELS) {
|
||||
AudioConverter converter(AudioConfig(aChannels, 0), AudioConfig(channels, 0));
|
||||
converter.Process(mDownmixBuffer, aBuffer, to_copy);
|
||||
@ -164,7 +159,7 @@ AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aFrame
|
||||
|
||||
#ifdef LOG_FAREND_INSERTION
|
||||
if (fp) {
|
||||
fwrite(&(mSaved->mData[mSamplesSaved * aChannels]), to_copy * aChannels, sizeof(int16_t), fp);
|
||||
fwrite(&(mSaved->mData[mSamplesSaved * aChannels]), to_copy * aChannels, sizeof(AudioDataValue), fp);
|
||||
}
|
||||
#endif
|
||||
aFrames -= to_copy;
|
||||
@ -187,7 +182,6 @@ AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aFrame
|
||||
}
|
||||
|
||||
MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource(
|
||||
webrtc::VoiceEngine* aVoiceEnginePtr,
|
||||
mozilla::AudioInput* aAudioInput,
|
||||
int aIndex,
|
||||
const char* name,
|
||||
@ -195,23 +189,21 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource(
|
||||
bool aDelayAgnostic,
|
||||
bool aExtendedFilter)
|
||||
: MediaEngineAudioSource(kReleased)
|
||||
, mVoiceEngine(aVoiceEnginePtr)
|
||||
, mAudioInput(aAudioInput)
|
||||
, mAudioProcessing(AudioProcessing::Create())
|
||||
, mAudioOutputObserver(new AudioOutputObserver())
|
||||
, mMonitor("WebRTCMic.Monitor")
|
||||
, mCapIndex(aIndex)
|
||||
, mChannel(-1)
|
||||
, mDelayAgnostic(aDelayAgnostic)
|
||||
, mExtendedFilter(aExtendedFilter)
|
||||
, mTrackID(TRACK_NONE)
|
||||
, mStarted(false)
|
||||
, mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE)
|
||||
, mSampleFrequency(MediaEngine::USE_GRAPH_RATE)
|
||||
, mTotalFrames(0)
|
||||
, mLastLogFrames(0)
|
||||
, mNullTransport(nullptr)
|
||||
, mSkipProcessing(false)
|
||||
, mInputDownmixBuffer(MAX_SAMPLING_FREQ * MAX_CHANNELS / 100)
|
||||
{
|
||||
MOZ_ASSERT(aVoiceEnginePtr);
|
||||
MOZ_ASSERT(aAudioInput);
|
||||
mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
|
||||
mDeviceUUID.Assign(uuid);
|
||||
@ -275,6 +267,140 @@ bool operator == (const MediaEnginePrefs& a, const MediaEnginePrefs& b)
|
||||
return !memcmp(&a, &b, sizeof(MediaEnginePrefs));
|
||||
};
|
||||
|
||||
// This does an early return in case of error.
|
||||
#define HANDLE_APM_ERROR(fn) \
|
||||
do { \
|
||||
int rv = fn; \
|
||||
if (rv != AudioProcessing::kNoError) { \
|
||||
MOZ_ASSERT_UNREACHABLE("APM error in " #fn); \
|
||||
return; \
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
void MediaEngineWebRTCMicrophoneSource::UpdateAECSettingsIfNeeded(bool aEnable, EcModes aMode)
|
||||
{
|
||||
using webrtc::EcModes;
|
||||
|
||||
EchoCancellation::SuppressionLevel level;
|
||||
|
||||
switch(aMode) {
|
||||
case EcModes::kEcUnchanged:
|
||||
level = mAudioProcessing->echo_cancellation()->suppression_level();
|
||||
break;
|
||||
case EcModes::kEcConference:
|
||||
level = EchoCancellation::kHighSuppression;
|
||||
break;
|
||||
case EcModes::kEcDefault:
|
||||
level = EchoCancellation::kModerateSuppression;
|
||||
break;
|
||||
case EcModes::kEcAec:
|
||||
level = EchoCancellation::kModerateSuppression;
|
||||
break;
|
||||
case EcModes::kEcAecm:
|
||||
// No suppression level to set for the mobile echo canceller
|
||||
break;
|
||||
default:
|
||||
MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, ("Bad EcMode value"));
|
||||
MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
|
||||
" for the echo cancelation mode.");
|
||||
// fall back to something sensible in release
|
||||
level = EchoCancellation::kModerateSuppression;
|
||||
break;
|
||||
}
|
||||
|
||||
// AECm and AEC are mutually exclusive.
|
||||
if (aMode == EcModes::kEcAecm) {
|
||||
HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->Enable(false));
|
||||
HANDLE_APM_ERROR(mAudioProcessing->echo_control_mobile()->Enable(aEnable));
|
||||
} else {
|
||||
HANDLE_APM_ERROR(mAudioProcessing->echo_control_mobile()->Enable(false));
|
||||
HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->Enable(aEnable));
|
||||
HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->set_suppression_level(level));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::UpdateAGCSettingsIfNeeded(bool aEnable, AgcModes aMode)
|
||||
{
|
||||
#if defined(WEBRTC_IOS) || defined(ATA) || defined(WEBRTC_ANDROID)
|
||||
if (aMode == kAgcAdaptiveAnalog) {
|
||||
MOZ_LOG(GetMediaManagerLog(),
|
||||
LogLevel::Error,
|
||||
("Invalid AGC mode kAgcAdaptiveAnalog on mobile"));
|
||||
MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
|
||||
" for the auto gain, on mobile.");
|
||||
aMode = kAgcDefault;
|
||||
}
|
||||
#endif
|
||||
GainControl::Mode mode = kDefaultAgcMode;
|
||||
|
||||
switch (aMode) {
|
||||
case AgcModes::kAgcDefault:
|
||||
mode = kDefaultAgcMode;
|
||||
break;
|
||||
case AgcModes::kAgcUnchanged:
|
||||
mode = mAudioProcessing->gain_control()->mode();
|
||||
break;
|
||||
case AgcModes::kAgcFixedDigital:
|
||||
mode = GainControl::Mode::kFixedDigital;
|
||||
break;
|
||||
case AgcModes::kAgcAdaptiveAnalog:
|
||||
mode = GainControl::Mode::kAdaptiveAnalog;
|
||||
break;
|
||||
case AgcModes::kAgcAdaptiveDigital:
|
||||
mode = GainControl::Mode::kAdaptiveDigital;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
|
||||
" for the auto gain.");
|
||||
// This is a good fallback, it works regardless of the platform.
|
||||
mode = GainControl::Mode::kAdaptiveDigital;
|
||||
break;
|
||||
}
|
||||
|
||||
HANDLE_APM_ERROR(mAudioProcessing->gain_control()->set_mode(mode));
|
||||
HANDLE_APM_ERROR(mAudioProcessing->gain_control()->Enable(aEnable));
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::UpdateNSSettingsIfNeeded(bool aEnable, NsModes aMode)
|
||||
{
|
||||
NoiseSuppression::Level nsLevel;
|
||||
|
||||
switch (aMode) {
|
||||
case NsModes::kNsDefault:
|
||||
nsLevel = kDefaultNsMode;
|
||||
break;
|
||||
case NsModes::kNsUnchanged:
|
||||
nsLevel = mAudioProcessing->noise_suppression()->level();
|
||||
break;
|
||||
case NsModes::kNsConference:
|
||||
nsLevel = NoiseSuppression::kHigh;
|
||||
break;
|
||||
case NsModes::kNsLowSuppression:
|
||||
nsLevel = NoiseSuppression::kLow;
|
||||
break;
|
||||
case NsModes::kNsModerateSuppression:
|
||||
nsLevel = NoiseSuppression::kModerate;
|
||||
break;
|
||||
case NsModes::kNsHighSuppression:
|
||||
nsLevel = NoiseSuppression::kHigh;
|
||||
break;
|
||||
case NsModes::kNsVeryHighSuppression:
|
||||
nsLevel = NoiseSuppression::kVeryHigh;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
|
||||
" for the noise suppression.");
|
||||
// Pick something sensible as a faillback in release.
|
||||
nsLevel = NoiseSuppression::kModerate;
|
||||
}
|
||||
HANDLE_APM_ERROR(mAudioProcessing->noise_suppression()->set_level(nsLevel));
|
||||
HANDLE_APM_ERROR(mAudioProcessing->noise_suppression()->Enable(aEnable));
|
||||
}
|
||||
|
||||
#undef HANDLE_APM_ERROR
|
||||
|
||||
nsresult
|
||||
MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
|
||||
const AllocationHandle* aHandle,
|
||||
@ -318,12 +444,7 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
|
||||
switch (mState) {
|
||||
case kReleased:
|
||||
MOZ_ASSERT(aHandle);
|
||||
if (sChannelsOpen == 0) {
|
||||
if (!InitEngine()) {
|
||||
LOG(("Audio engine is not initalized"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else {
|
||||
if (sChannelsOpen != 0) {
|
||||
// Until we fix (or wallpaper) support for multiple mic input
|
||||
// (Bug 1238038) fail allocation for a second device
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -355,19 +476,19 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
|
||||
|
||||
if (prefs.mChannels != mLastPrefs.mChannels) {
|
||||
MOZ_ASSERT(mSources.Length() > 0);
|
||||
// If the channel count changed, tell the MSG to open a new driver with
|
||||
// the correct channel count.
|
||||
auto& source = mSources.LastElement();
|
||||
mAudioInput->SetUserChannelCount(prefs.mChannels);
|
||||
// Get validated number of channel
|
||||
uint32_t channelCount = 0;
|
||||
mAudioInput->GetChannelCount(channelCount);
|
||||
MOZ_ASSERT(channelCount > 0 && mLastPrefs.mChannels > 0);
|
||||
// Check if new validated channels is the same as previous
|
||||
if (static_cast<uint32_t>(mLastPrefs.mChannels) != channelCount &&
|
||||
if (mLastPrefs.mChannels != prefs.mChannels &&
|
||||
!source->OpenNewAudioCallbackDriver(mListener)) {
|
||||
MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, ("Could not open a new AudioCallbackDriver for input"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// Update settings
|
||||
prefs.mChannels = channelCount;
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
|
||||
@ -386,49 +507,23 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
|
||||
}
|
||||
|
||||
if (sChannelsOpen > 0) {
|
||||
int error;
|
||||
UpdateAGCSettingsIfNeeded(prefs.mAgcOn, static_cast<AgcModes>(prefs.mAgc));
|
||||
UpdateNSSettingsIfNeeded(prefs.mNoiseOn, static_cast<NsModes>(prefs.mNoise));
|
||||
UpdateAECSettingsIfNeeded(prefs.mAecOn, static_cast<EcModes>(prefs.mAec));
|
||||
|
||||
error = mVoEProcessing->SetEcStatus(prefs.mAecOn, (webrtc::EcModes)prefs.mAec);
|
||||
if (error) {
|
||||
LOG(("%s Error setting Echo Status: %d ",__FUNCTION__, error));
|
||||
// Overhead of capturing all the time is very low (<0.1% of an audio only call)
|
||||
if (prefs.mAecOn) {
|
||||
error = mVoEProcessing->SetEcMetricsStatus(true);
|
||||
if (error) {
|
||||
LOG(("%s Error setting Echo Metrics: %d ",__FUNCTION__, error));
|
||||
}
|
||||
}
|
||||
}
|
||||
error = mVoEProcessing->SetAgcStatus(prefs.mAgcOn, (webrtc::AgcModes)prefs.mAgc);
|
||||
if (error) {
|
||||
LOG(("%s Error setting AGC Status: %d ",__FUNCTION__, error));
|
||||
}
|
||||
error = mVoEProcessing->SetNsStatus(prefs.mNoiseOn, (webrtc::NsModes)prefs.mNoise);
|
||||
if (error) {
|
||||
LOG(("%s Error setting NoiseSuppression Status: %d ",__FUNCTION__, error));
|
||||
}
|
||||
}
|
||||
|
||||
// we don't allow switching from non-fast-path to fast-path on the fly yet
|
||||
if (mState != kStarted) {
|
||||
mSkipProcessing = !(prefs.mAecOn || prefs.mAgcOn || prefs.mNoiseOn);
|
||||
if (mSkipProcessing) {
|
||||
mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
|
||||
} else {
|
||||
// make sure we route a copy of the mixed audio output of this MSG to the
|
||||
// AEC
|
||||
if (!mAudioOutputObserver) {
|
||||
mAudioOutputObserver = new AudioOutputObserver();
|
||||
}
|
||||
}
|
||||
webrtc::Config config;
|
||||
config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(mExtendedFilter));
|
||||
config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(mDelayAgnostic));
|
||||
mAudioProcessing->SetExtraOptions(config);
|
||||
}
|
||||
SetLastPrefs(prefs);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#undef HANDLE_APM_ERROR
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::SetLastPrefs(
|
||||
const MediaEnginePrefs& aPrefs)
|
||||
MediaEngineWebRTCMicrophoneSource::SetLastPrefs(const MediaEnginePrefs& aPrefs)
|
||||
{
|
||||
mLastPrefs = aPrefs;
|
||||
|
||||
@ -439,11 +534,35 @@ MediaEngineWebRTCMicrophoneSource::SetLastPrefs(
|
||||
that->mSettings->mAutoGainControl.Value() = aPrefs.mAgcOn;
|
||||
that->mSettings->mNoiseSuppression.Value() = aPrefs.mNoiseOn;
|
||||
that->mSettings->mChannelCount.Value() = aPrefs.mChannels;
|
||||
|
||||
class Message : public ControlMessage {
|
||||
public:
|
||||
Message(MediaEngineWebRTCMicrophoneSource* aSource,
|
||||
bool aPassThrough)
|
||||
: ControlMessage(nullptr)
|
||||
, mMicrophoneSource(aSource)
|
||||
, mPassThrough(aPassThrough)
|
||||
{}
|
||||
|
||||
void Run() override
|
||||
{
|
||||
mMicrophoneSource->SetPassThrough(mPassThrough);
|
||||
}
|
||||
|
||||
protected:
|
||||
RefPtr<MediaEngineWebRTCMicrophoneSource> mMicrophoneSource;
|
||||
bool mPassThrough;
|
||||
};
|
||||
|
||||
bool passThrough = !(aPrefs.mAecOn || aPrefs.mAgcOn || aPrefs.mNoiseOn);
|
||||
if (!that->mSources.IsEmpty()) {
|
||||
that->mSources[0]->GraphImpl()->AppendMessage(MakeUnique<Message>(that, passThrough));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
MediaEngineWebRTCMicrophoneSource::Deallocate(AllocationHandle* aHandle)
|
||||
{
|
||||
@ -493,7 +612,7 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream,
|
||||
if (mSampleFrequency == MediaEngine::USE_GRAPH_RATE) {
|
||||
mSampleFrequency = aStream->GraphRate();
|
||||
}
|
||||
aStream->AddAudioTrack(aID, mSampleFrequency, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
|
||||
aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
|
||||
|
||||
// XXX Make this based on the pref.
|
||||
aStream->RegisterForAudioMixing();
|
||||
@ -514,24 +633,10 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream,
|
||||
// Make sure logger starts before capture
|
||||
AsyncLatencyLogger::Get(true);
|
||||
|
||||
if (mAudioOutputObserver) {
|
||||
mAudioOutputObserver->Clear();
|
||||
}
|
||||
mAudioOutputObserver->Clear();
|
||||
|
||||
if (mVoEBase->StartReceive(mChannel)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Must be *before* StartSend() so it will notice we selected external input (full_duplex)
|
||||
mAudioInput->StartRecording(aStream, mListener);
|
||||
|
||||
if (mVoEBase->StartSend(mChannel)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Attach external media processor, so this::Process will be called.
|
||||
mVoERender->RegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel, *this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -560,9 +665,6 @@ MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
||||
if (mState != kStarted) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!mVoEBase) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mState = kStopped;
|
||||
}
|
||||
@ -574,14 +676,6 @@ MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
||||
|
||||
mAudioInput->StopRecording(aSource);
|
||||
|
||||
mVoERender->DeRegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel);
|
||||
|
||||
if (mVoEBase->StopSend(mChannel)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (mVoEBase->StopReceive(mChannel)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -603,12 +697,13 @@ MediaEngineWebRTCMicrophoneSource::NotifyOutputData(MediaStreamGraph* aGraph,
|
||||
TrackRate aRate,
|
||||
uint32_t aChannels)
|
||||
{
|
||||
if (mAudioOutputObserver) {
|
||||
if (!PassThrough()) {
|
||||
mAudioOutputObserver->InsertFarEnd(aBuffer, aFrames, false,
|
||||
aRate, aChannels);
|
||||
aRate, aChannels);
|
||||
}
|
||||
}
|
||||
|
||||
// Only called if we're not in passthrough mode
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph,
|
||||
const AudioDataValue* aBuffer,
|
||||
@ -616,15 +711,131 @@ MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph,
|
||||
TrackRate aRate,
|
||||
uint32_t aChannels)
|
||||
{
|
||||
// This will call Process() with data coming out of the AEC/NS/AGC/etc chain
|
||||
MOZ_ASSERT(!PassThrough(), "This should be bypassed when in PassThrough mode.");
|
||||
size_t offset = 0;
|
||||
|
||||
if (!mPacketizer ||
|
||||
mPacketizer->PacketSize() != aRate/100u ||
|
||||
mPacketizer->Channels() != aChannels) {
|
||||
// It's ok to drop the audio still in the packetizer here.
|
||||
mPacketizer =
|
||||
new AudioPacketizer<AudioDataValue, int16_t>(aRate/100, aChannels);
|
||||
new AudioPacketizer<AudioDataValue, float>(aRate/100, aChannels);
|
||||
}
|
||||
|
||||
// On initial capture, throw away all far-end data except the most recent sample
|
||||
// since it's already irrelevant and we want to keep avoid confusing the AEC far-end
|
||||
// input code with "old" audio.
|
||||
if (!mStarted) {
|
||||
mStarted = true;
|
||||
while (mAudioOutputObserver->Size() > 1) {
|
||||
free(mAudioOutputObserver->Pop()); // only call if size() > 0
|
||||
}
|
||||
}
|
||||
|
||||
// Feed the far-end audio data (speakers) to the feedback input of the AEC.
|
||||
while (mAudioOutputObserver->Size() > 0) {
|
||||
// Bug 1414837: This will call `free()`, and we should remove it.
|
||||
// Pop gives ownership.
|
||||
nsAutoPtr<FarEndAudioChunk> buffer(mAudioOutputObserver->Pop()); // only call if size() > 0
|
||||
if (!buffer) {
|
||||
continue;
|
||||
}
|
||||
AudioDataValue* packetDataPointer = buffer->mData;
|
||||
AutoTArray<AudioDataValue*, MAX_CHANNELS> deinterleavedPacketDataChannelPointers;
|
||||
AudioDataValue* interleavedFarend = nullptr;
|
||||
uint32_t channelCountFarend = 0;
|
||||
uint32_t framesPerPacketFarend = 0;
|
||||
|
||||
// Downmix from aChannels to MAX_CHANNELS if needed
|
||||
if (mAudioOutputObserver->PlayoutChannels() > MAX_CHANNELS) {
|
||||
AudioConverter converter(AudioConfig(aChannels, 0, AudioConfig::FORMAT_DEFAULT),
|
||||
AudioConfig(MAX_CHANNELS, 0, AudioConfig::FORMAT_DEFAULT));
|
||||
framesPerPacketFarend =
|
||||
buffer->mSamples;
|
||||
framesPerPacketFarend =
|
||||
converter.Process(mInputDownmixBuffer,
|
||||
packetDataPointer,
|
||||
framesPerPacketFarend);
|
||||
interleavedFarend = mInputDownmixBuffer.Data();
|
||||
channelCountFarend = MAX_CHANNELS;
|
||||
deinterleavedPacketDataChannelPointers.SetLength(MAX_CHANNELS);
|
||||
} else {
|
||||
uint32_t outputChannels = mAudioOutputObserver->PlayoutChannels();
|
||||
interleavedFarend = packetDataPointer;
|
||||
channelCountFarend = outputChannels;
|
||||
framesPerPacketFarend = buffer->mSamples;
|
||||
deinterleavedPacketDataChannelPointers.SetLength(outputChannels);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(interleavedFarend &&
|
||||
(channelCountFarend == 1 || channelCountFarend == 2) &&
|
||||
framesPerPacketFarend);
|
||||
|
||||
offset = 0;
|
||||
for (size_t i = 0; i < deinterleavedPacketDataChannelPointers.Length(); ++i) {
|
||||
deinterleavedPacketDataChannelPointers[i] = packetDataPointer + offset;
|
||||
offset += framesPerPacketFarend;
|
||||
}
|
||||
|
||||
// deinterleave back into the FarEndAudioChunk buffer to save an alloc.
|
||||
// There is enough room because either there is the same number of
|
||||
// channels/frames or we've just downmixed.
|
||||
Deinterleave(interleavedFarend,
|
||||
framesPerPacketFarend,
|
||||
channelCountFarend,
|
||||
deinterleavedPacketDataChannelPointers.Elements());
|
||||
|
||||
// Having the same config for input and output means we potentially save
|
||||
// some CPU. We won't need the output here, the API forces us to set a
|
||||
// valid pointer with enough space.
|
||||
StreamConfig inputConfig(mAudioOutputObserver->PlayoutFrequency(),
|
||||
channelCountFarend,
|
||||
false /* we don't use typing detection*/);
|
||||
StreamConfig outputConfig = inputConfig;
|
||||
|
||||
// Prepare a channel pointers array, with enough storage for the
|
||||
// frames.
|
||||
//
|
||||
// If this is a platform that uses s16 for audio input and output,
|
||||
// convert to floats, the APM API we use only accepts floats.
|
||||
|
||||
float* inputData = nullptr;
|
||||
#ifdef MOZ_SAMPLE_TYPE_S16
|
||||
// Convert to floats, use mInputBuffer for this.
|
||||
size_t sampleCount = framesPerPacketFarend * channelCountFarend;
|
||||
if (mInputBuffer.Length() < sampleCount) {
|
||||
mInputBuffer.SetLength(sampleCount);
|
||||
}
|
||||
ConvertAudioSamples(buffer->mData, mInputBuffer.Data(), sampleCount);
|
||||
inputData = mInputBuffer.Data();
|
||||
#else // MOZ_SAMPLE_TYPE_F32
|
||||
inputData = buffer->mData;
|
||||
#endif
|
||||
|
||||
AutoTArray<float*, MAX_CHANNELS> channelsPointers;
|
||||
channelsPointers.SetLength(channelCountFarend);
|
||||
offset = 0;
|
||||
for (size_t i = 0; i < channelsPointers.Length(); ++i) {
|
||||
channelsPointers[i] = inputData + offset;
|
||||
offset += framesPerPacketFarend;
|
||||
}
|
||||
|
||||
// Passing the same pointers here saves a copy inside this function.
|
||||
int err =
|
||||
mAudioProcessing->ProcessReverseStream(channelsPointers.Elements(),
|
||||
inputConfig,
|
||||
outputConfig,
|
||||
channelsPointers.Elements());
|
||||
|
||||
if (err) {
|
||||
MOZ_LOG(GetMediaManagerLog(), LogLevel::Error,
|
||||
("error in audio ProcessReverseStream(): %d", err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Packetize our input data into 10ms chunks, deinterleave into planar channel
|
||||
// buffers, process, and append to the right MediaStreamTrack.
|
||||
mPacketizer->Input(aBuffer, static_cast<uint32_t>(aFrames));
|
||||
|
||||
while (mPacketizer->PacketsAvailable()) {
|
||||
@ -632,19 +843,73 @@ MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph,
|
||||
mPacketizer->Channels();
|
||||
if (mInputBuffer.Length() < samplesPerPacket) {
|
||||
mInputBuffer.SetLength(samplesPerPacket);
|
||||
mDeinterleavedBuffer.SetLength(samplesPerPacket);
|
||||
}
|
||||
int16_t* packet = mInputBuffer.Elements();
|
||||
float* packet = mInputBuffer.Data();
|
||||
mPacketizer->Output(packet);
|
||||
|
||||
if (aChannels > MAX_CHANNELS) {
|
||||
AudioConverter converter(AudioConfig(aChannels, 0, AudioConfig::FORMAT_S16),
|
||||
AudioConfig(MAX_CHANNELS, 0, AudioConfig::FORMAT_S16));
|
||||
converter.Process(mInputDownmixBuffer, packet, mPacketizer->PacketSize());
|
||||
mVoERender->ExternalRecordingInsertData(mInputDownmixBuffer.Data(),
|
||||
mPacketizer->PacketSize() * MAX_CHANNELS,
|
||||
aRate, 0);
|
||||
} else {
|
||||
mVoERender->ExternalRecordingInsertData(packet, samplesPerPacket, aRate, 0);
|
||||
// Deinterleave the input data
|
||||
// Prepare an array pointing to deinterleaved channels.
|
||||
AutoTArray<float*, 8> deinterleavedPacketizedInputDataChannelPointers;
|
||||
deinterleavedPacketizedInputDataChannelPointers.SetLength(aChannels);
|
||||
offset = 0;
|
||||
for (size_t i = 0; i < deinterleavedPacketizedInputDataChannelPointers.Length(); ++i) {
|
||||
deinterleavedPacketizedInputDataChannelPointers[i] = mDeinterleavedBuffer.Data() + offset;
|
||||
offset += aFrames;
|
||||
}
|
||||
|
||||
// Deinterleave to mInputBuffer, pointed to by inputBufferChannelPointers.
|
||||
Deinterleave(packet, mPacketizer->PacketSize(), aChannels,
|
||||
deinterleavedPacketizedInputDataChannelPointers.Elements());
|
||||
|
||||
StreamConfig inputConfig(aRate,
|
||||
aChannels,
|
||||
false /* we don't use typing detection*/);
|
||||
StreamConfig outputConfig = inputConfig;
|
||||
|
||||
// Bug 1404965: Get the right delay here, it saves some work down the line.
|
||||
mAudioProcessing->set_stream_delay_ms(0);
|
||||
|
||||
// Bug 1414837: find a way to not allocate here.
|
||||
RefPtr<SharedBuffer> buffer =
|
||||
SharedBuffer::Create(mPacketizer->PacketSize() * aChannels * sizeof(float));
|
||||
AudioSegment segment;
|
||||
|
||||
// Prepare channel pointers to the SharedBuffer created above.
|
||||
AutoTArray<float*, 8> processedOutputChannelPointers;
|
||||
AutoTArray<const float*, 8> processedOutputChannelPointersConst;
|
||||
processedOutputChannelPointers.SetLength(aChannels);
|
||||
processedOutputChannelPointersConst.SetLength(aChannels);
|
||||
|
||||
offset = 0;
|
||||
for (size_t i = 0; i < processedOutputChannelPointers.Length(); ++i) {
|
||||
processedOutputChannelPointers[i] = static_cast<float*>(buffer->Data()) + offset;
|
||||
processedOutputChannelPointersConst[i] = static_cast<float*>(buffer->Data()) + offset;
|
||||
offset += aFrames;
|
||||
}
|
||||
|
||||
mAudioProcessing->ProcessStream(deinterleavedPacketizedInputDataChannelPointers.Elements(),
|
||||
inputConfig,
|
||||
outputConfig,
|
||||
processedOutputChannelPointers.Elements());
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (mState != kStarted)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < mSources.Length(); ++i) {
|
||||
if (!mSources[i]) { // why ?!
|
||||
continue;
|
||||
}
|
||||
|
||||
// We already have planar audio data of the right format. Insert into the
|
||||
// MSG.
|
||||
MOZ_ASSERT(processedOutputChannelPointers.Length() == aChannels);
|
||||
RefPtr<SharedBuffer> other = buffer;
|
||||
segment.AppendFrames(other.forget(),
|
||||
processedOutputChannelPointersConst,
|
||||
mPacketizer->PacketSize(),
|
||||
mPrincipalHandles[i]);
|
||||
mSources[i]->AppendToTrack(mTrackID, &segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -671,7 +936,7 @@ MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer,
|
||||
}
|
||||
|
||||
size_t len = mSources.Length();
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (!mSources[i]) {
|
||||
continue;
|
||||
}
|
||||
@ -687,7 +952,7 @@ MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer,
|
||||
MOZ_ASSERT(aChannels >= 1 && aChannels <= 8,
|
||||
"Support up to 8 channels");
|
||||
|
||||
nsAutoPtr<AudioSegment> segment(new AudioSegment());
|
||||
AudioSegment segment;
|
||||
RefPtr<SharedBuffer> buffer =
|
||||
SharedBuffer::Create(aFrames * aChannels * sizeof(T));
|
||||
AutoTArray<const T*, 8> channels;
|
||||
@ -713,11 +978,11 @@ MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer,
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aChannels == channels.Length());
|
||||
segment->AppendFrames(buffer.forget(), channels, aFrames,
|
||||
segment.AppendFrames(buffer.forget(), channels, aFrames,
|
||||
mPrincipalHandles[i]);
|
||||
segment->GetStartTime(insertTime);
|
||||
segment.GetStartTime(insertTime);
|
||||
|
||||
mSources[i]->AppendToTrack(mTrackID, segment);
|
||||
mSources[i]->AppendToTrack(mTrackID, &segment);
|
||||
}
|
||||
}
|
||||
|
||||
@ -741,164 +1006,54 @@ MediaEngineWebRTCMicrophoneSource::NotifyInputData(MediaStreamGraph* aGraph,
|
||||
|
||||
#define ResetProcessingIfNeeded(_processing) \
|
||||
do { \
|
||||
webrtc::_processing##Modes mode; \
|
||||
int rv = mVoEProcessing->Get##_processing##Status(enabled, mode); \
|
||||
if (rv) { \
|
||||
NS_WARNING("Could not get the status of the " \
|
||||
#_processing " on device change."); \
|
||||
return; \
|
||||
} \
|
||||
bool enabled = mAudioProcessing->_processing()->is_enabled(); \
|
||||
\
|
||||
if (enabled) { \
|
||||
rv = mVoEProcessing->Set##_processing##Status(!enabled); \
|
||||
int rv = mAudioProcessing->_processing()->Enable(!enabled); \
|
||||
if (rv) { \
|
||||
NS_WARNING("Could not reset the status of the " \
|
||||
#_processing " on device change."); \
|
||||
return; \
|
||||
} \
|
||||
rv = mAudioProcessing->_processing()->Enable(enabled); \
|
||||
if (rv) { \
|
||||
NS_WARNING("Could not reset the status of the " \
|
||||
#_processing " on device change."); \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
rv = mVoEProcessing->Set##_processing##Status(enabled); \
|
||||
if (rv) { \
|
||||
NS_WARNING("Could not reset the status of the " \
|
||||
#_processing " on device change."); \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::DeviceChanged() {
|
||||
// Reset some processing
|
||||
bool enabled;
|
||||
ResetProcessingIfNeeded(Agc);
|
||||
ResetProcessingIfNeeded(Ec);
|
||||
ResetProcessingIfNeeded(Ns);
|
||||
ResetProcessingIfNeeded(gain_control);
|
||||
ResetProcessingIfNeeded(echo_cancellation);
|
||||
ResetProcessingIfNeeded(noise_suppression);
|
||||
}
|
||||
|
||||
bool
|
||||
MediaEngineWebRTCMicrophoneSource::InitEngine()
|
||||
{
|
||||
MOZ_ASSERT(!mVoEBase);
|
||||
mVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
|
||||
|
||||
mVoEBase->Init();
|
||||
webrtc::Config config;
|
||||
config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(mExtendedFilter));
|
||||
config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(mDelayAgnostic));
|
||||
mVoEBase->audio_processing()->SetExtraOptions(config);
|
||||
|
||||
mVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
|
||||
if (mVoERender) {
|
||||
mVoENetwork = webrtc::VoENetwork::GetInterface(mVoiceEngine);
|
||||
if (mVoENetwork) {
|
||||
mVoEProcessing = webrtc::VoEAudioProcessing::GetInterface(mVoiceEngine);
|
||||
if (mVoEProcessing) {
|
||||
mNullTransport = new NullTransport();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
DeInitEngine();
|
||||
return false;
|
||||
}
|
||||
|
||||
// This shuts down the engine when no channel is open
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::DeInitEngine()
|
||||
{
|
||||
if (mVoEBase) {
|
||||
mVoEBase->Terminate();
|
||||
delete mNullTransport;
|
||||
mNullTransport = nullptr;
|
||||
|
||||
mVoEProcessing = nullptr;
|
||||
mVoENetwork = nullptr;
|
||||
mVoERender = nullptr;
|
||||
mVoEBase = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// This shuts down the engine when no channel is open.
|
||||
// mState records if a channel is allocated (slightly redundantly to mChannel)
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::FreeChannel()
|
||||
{
|
||||
if (mState != kReleased) {
|
||||
if (mChannel != -1) {
|
||||
MOZ_ASSERT(mVoENetwork && mVoEBase);
|
||||
if (mVoENetwork) {
|
||||
mVoENetwork->DeRegisterExternalTransport(mChannel);
|
||||
}
|
||||
if (mVoEBase) {
|
||||
mVoEBase->DeleteChannel(mChannel);
|
||||
}
|
||||
mChannel = -1;
|
||||
}
|
||||
mState = kReleased;
|
||||
|
||||
MOZ_ASSERT(sChannelsOpen > 0);
|
||||
if (--sChannelsOpen == 0) {
|
||||
DeInitEngine();
|
||||
}
|
||||
--sChannelsOpen;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MediaEngineWebRTCMicrophoneSource::AllocChannel()
|
||||
{
|
||||
MOZ_ASSERT(mVoEBase);
|
||||
mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
|
||||
LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency));
|
||||
|
||||
mChannel = mVoEBase->CreateChannel();
|
||||
if (mChannel >= 0) {
|
||||
if (!mVoENetwork->RegisterExternalTransport(mChannel, *mNullTransport)) {
|
||||
mSampleFrequency = MediaEngine::DEFAULT_SAMPLE_RATE;
|
||||
LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency));
|
||||
|
||||
// Check for availability.
|
||||
if (!mAudioInput->SetRecordingDevice(mCapIndex)) {
|
||||
// Because of the permission mechanism of B2G, we need to skip the status
|
||||
// check here.
|
||||
bool avail = false;
|
||||
mAudioInput->GetRecordingDeviceStatus(avail);
|
||||
if (!avail) {
|
||||
if (sChannelsOpen == 0) {
|
||||
DeInitEngine();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set "codec" to PCM, 32kHz on device's channels
|
||||
ScopedCustomReleasePtr<webrtc::VoECodec> ptrVoECodec(webrtc::VoECodec::GetInterface(mVoiceEngine));
|
||||
if (ptrVoECodec) {
|
||||
webrtc::CodecInst codec;
|
||||
strcpy(codec.plname, ENCODING);
|
||||
codec.channels = CHANNELS;
|
||||
uint32_t maxChannels = 0;
|
||||
if (mAudioInput->GetMaxAvailableChannels(maxChannels) == 0) {
|
||||
MOZ_ASSERT(maxChannels);
|
||||
codec.channels = std::min<uint32_t>(maxChannels, MAX_CHANNELS);
|
||||
}
|
||||
MOZ_ASSERT(mSampleFrequency == 16000 || mSampleFrequency == 32000);
|
||||
codec.rate = SAMPLE_RATE(mSampleFrequency);
|
||||
codec.plfreq = mSampleFrequency;
|
||||
codec.pacsize = SAMPLE_LENGTH(mSampleFrequency);
|
||||
codec.pltype = 0; // Default payload type
|
||||
|
||||
if (!ptrVoECodec->SetSendCodec(mChannel, codec)) {
|
||||
mState = kAllocated;
|
||||
sChannelsOpen++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mVoEBase->DeleteChannel(mChannel);
|
||||
mChannel = -1;
|
||||
if (sChannelsOpen == 0) {
|
||||
DeInitEngine();
|
||||
}
|
||||
return false;
|
||||
mState = kAllocated;
|
||||
sChannelsOpen++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@ -936,46 +1091,6 @@ MediaEngineWebRTCMicrophoneSource::Shutdown()
|
||||
Deallocate(mRegisteredHandles[0].get());
|
||||
}
|
||||
MOZ_ASSERT(mState == kReleased);
|
||||
|
||||
mAudioInput = nullptr;
|
||||
}
|
||||
|
||||
typedef int16_t sample;
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::Process(int channel,
|
||||
webrtc::ProcessingTypes type,
|
||||
sample *audio10ms, size_t length,
|
||||
int samplingFreq, bool isStereo)
|
||||
{
|
||||
MOZ_ASSERT(!PassThrough(), "This should be bypassed when in PassThrough mode.");
|
||||
// On initial capture, throw away all far-end data except the most recent sample
|
||||
// since it's already irrelevant and we want to keep avoid confusing the AEC far-end
|
||||
// input code with "old" audio.
|
||||
if (!mStarted) {
|
||||
mStarted = true;
|
||||
while (mAudioOutputObserver->Size() > 1) {
|
||||
free(mAudioOutputObserver->Pop()); // only call if size() > 0
|
||||
}
|
||||
}
|
||||
|
||||
while (mAudioOutputObserver->Size() > 0) {
|
||||
FarEndAudioChunk *buffer = mAudioOutputObserver->Pop(); // only call if size() > 0
|
||||
if (buffer) {
|
||||
int length = buffer->mSamples;
|
||||
int res = mVoERender->ExternalPlayoutData(buffer->mData,
|
||||
mAudioOutputObserver->PlayoutFrequency(),
|
||||
mAudioOutputObserver->PlayoutChannels(),
|
||||
length);
|
||||
free(buffer);
|
||||
if (res == -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t channels = isStereo ? 2 : 1;
|
||||
InsertInGraph<int16_t>(audio10ms, length, channels);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -39,11 +39,12 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
'MediaEngineWebRTC.cpp',
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
'..',
|
||||
'/dom/base',
|
||||
'/media/libyuv/libyuv/include',
|
||||
'/media/webrtc/signaling/src/common',
|
||||
'/media/webrtc/signaling/src/common/browser_logging',
|
||||
'/media/webrtc/trunk',
|
||||
'/media/webrtc/trunk'
|
||||
]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
|
@ -141,6 +141,17 @@ PerformanceMainThread::AddEntry(nsIHttpChannel* channel,
|
||||
originalURI->GetSpec(name);
|
||||
NS_ConvertUTF8toUTF16 entryName(name);
|
||||
|
||||
bool reportTiming = true;
|
||||
timedChannel->GetReportResourceTiming(&reportTiming);
|
||||
|
||||
if (!reportTiming) {
|
||||
#ifdef DEBUG_jwatt
|
||||
NS_WARNING(
|
||||
nsPrintfCString("Not reporting CORS resource: %s", name.get()).get());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// The nsITimedChannel argument will be used to gather all the timings.
|
||||
// The nsIHttpChannel argument will be used to check if any cross-origin
|
||||
// redirects occurred.
|
||||
|
@ -52,6 +52,23 @@ PerformanceTiming::PerformanceTiming(Performance* aPerformance,
|
||||
mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck;
|
||||
}
|
||||
|
||||
mSecureConnection = false;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (aHttpChannel) {
|
||||
aHttpChannel->GetURI(getter_AddRefs(uri));
|
||||
} else {
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
||||
if (httpChannel) {
|
||||
httpChannel->GetURI(getter_AddRefs(uri));
|
||||
}
|
||||
}
|
||||
|
||||
if (uri) {
|
||||
nsresult rv = uri->SchemeIs("https", &mSecureConnection);
|
||||
if (NS_FAILED(rv)) {
|
||||
mSecureConnection = false;
|
||||
}
|
||||
}
|
||||
InitializeTimingInfo(aChannel);
|
||||
|
||||
// Non-null aHttpChannel implies that this PerformanceTiming object is being
|
||||
@ -118,7 +135,8 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
|
||||
mConnectStart = *clampTime;
|
||||
}
|
||||
|
||||
if (!mSecureConnectionStart.IsNull() && mSecureConnectionStart < *clampTime) {
|
||||
if (mSecureConnection && !mSecureConnectionStart.IsNull() &&
|
||||
mSecureConnectionStart < *clampTime) {
|
||||
mSecureConnectionStart = *clampTime;
|
||||
}
|
||||
|
||||
@ -369,8 +387,11 @@ PerformanceTiming::SecureConnectionStartHighRes()
|
||||
nsContentUtils::ShouldResistFingerprinting()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
return mSecureConnectionStart.IsNull() ? mZeroTime
|
||||
: TimeStampToDOMHighRes(mSecureConnectionStart);
|
||||
return !mSecureConnection
|
||||
? 0 // We use 0 here, because mZeroTime is sometimes set to the navigation
|
||||
// start time.
|
||||
: (mSecureConnectionStart.IsNull() ? mZeroTime
|
||||
: TimeStampToDOMHighRes(mSecureConnectionStart));
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
|
@ -305,6 +305,8 @@ private:
|
||||
// redirectEnd attributes. It is false if there were no redirects, or if
|
||||
// any of the responses didn't pass the timing-allow-check
|
||||
bool mReportCrossOriginRedirect;
|
||||
|
||||
bool mSecureConnection;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -266,7 +266,7 @@ var steps = [
|
||||
performance.measure("test", n);
|
||||
ok(true, "Measure created from reserved name as starting time: " + n);
|
||||
} catch (e) {
|
||||
ok(["redirectStart", "redirectEnd", "unloadEventStart", "unloadEventEnd", "loadEventEnd"].indexOf(n) >= 0,
|
||||
ok(["redirectStart", "redirectEnd", "unloadEventStart", "unloadEventEnd", "loadEventEnd", "secureConnectionStart"].indexOf(n) >= 0,
|
||||
"Measure created from reserved name as starting time: " + n + " and threw expected error");
|
||||
}
|
||||
};
|
||||
|
@ -56,7 +56,7 @@ public:
|
||||
virtual void GetLinkTarget(nsAString& aTarget) override;
|
||||
virtual already_AddRefed<nsIURI> GetHrefURI() const override;
|
||||
virtual EventStates IntrinsicState() const override;
|
||||
using nsIContent::SetAttr;
|
||||
using Element::SetAttr;
|
||||
virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||
nsAtom* aPrefix, const nsAString& aValue,
|
||||
nsIPrincipal* aSubjectPrincipal,
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
bool aCompileEventHandlers) override;
|
||||
virtual void UnbindFromTree(bool aDeep = true,
|
||||
bool aNullParent = true) override;
|
||||
using nsIContent::SetAttr;
|
||||
using Element::SetAttr;
|
||||
virtual nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||
nsAtom* aPrefix, const nsAString& aValue,
|
||||
nsIPrincipal* aSubjectPrincipal,
|
||||
|
0
dom/tests/mochitest/general/cssA.css
Normal file
0
dom/tests/mochitest/general/cssA.css
Normal file
2
dom/tests/mochitest/general/cssB.css
Normal file
2
dom/tests/mochitest/general/cssB.css
Normal file
@ -0,0 +1,2 @@
|
||||
@import 'cssC.css';
|
||||
@import url('http://example.org/tests/dom/tests/mochitest/general/cssC.css');
|
0
dom/tests/mochitest/general/cssC.css
Normal file
0
dom/tests/mochitest/general/cssC.css
Normal file
1
dom/tests/mochitest/general/emptyCssFile2.css
Normal file
1
dom/tests/mochitest/general/emptyCssFile2.css
Normal file
@ -0,0 +1 @@
|
||||
@import url('http://example.org/tests/dom/tests/mochitest/general/cross.css');
|
191
dom/tests/mochitest/general/file_resource_timing_nocors.html
Normal file
191
dom/tests/mochitest/general/file_resource_timing_nocors.html
Normal file
@ -0,0 +1,191 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css"/>
|
||||
|
||||
<!--
|
||||
This document has the origin http://mochi.test:8888.
|
||||
|
||||
The resource timing of any all sub-resources should be reported, except for
|
||||
any sub-resources of any cross-origin resources that are loaded.
|
||||
|
||||
Note that the resource timing of the original cross-origin resource should
|
||||
itself be reported. The goal here is to not reveal which sub-resources are
|
||||
loaded by any cross-origin resources (the fact that any given cross-origin
|
||||
resource itself is loaded is known to the original origin).
|
||||
|
||||
In the comments below, the following annotations apply:
|
||||
|
||||
[r] - this resource should be reported by performance.getEntries()
|
||||
[!] - this resource should be excluded from performance.getEntries()
|
||||
-->
|
||||
|
||||
|
||||
<!-- 1. [r] http://mochi.test:8888 , generateCss.sjs?A
|
||||
[r] http://mochi.test:8888 , generateCss.sjs?B
|
||||
-->
|
||||
<link rel="stylesheet" type="text/css" href="generateCss.sjs?A"/>
|
||||
|
||||
|
||||
<!-- 2. [r] http://example.com , generateCss.sjs?C
|
||||
[!] http://example.com , generateCss.sjs?D
|
||||
-->
|
||||
<link rel="stylesheet" type="text/css" href="http://example.com/tests/dom/tests/mochitest/general/generateCss.sjs?C"/>
|
||||
|
||||
|
||||
<!-- 3. [r] http://example.com , generateCss.sjs?E
|
||||
[!] http://mochi.test:8888 , generateCss.sjs?F
|
||||
-->
|
||||
<link rel="stylesheet" type="text/css" href="http://example.com/tests/dom/tests/mochitest/general/generateCss.sjs?E"/>
|
||||
|
||||
|
||||
<!-- 4. [r] http://mochi.test:8888 , generateCss.sjs?G
|
||||
[r] http://mochi.test:8888 , generateCss.sjs?H
|
||||
[r] http://example.com , generateCss.sjs?I
|
||||
[!] http://example.com , generateCss.sjs?J
|
||||
[r] http://example.org , generateCss.sjs?K
|
||||
[!] http://example.org , generateCss.sjs?L
|
||||
[!] http://example.org , generateCss.sjs?M
|
||||
-->
|
||||
<link rel="stylesheet" type="text/css" href="generateCss.sjs?G"/>
|
||||
|
||||
|
||||
<!-- 5. background-image: -moz-image-rect()
|
||||
[r] http://example.net , generateCss.sjs?N
|
||||
[!] http://example.net , blue.png
|
||||
-->
|
||||
<link rel="stylesheet" type="text/css" href="http://example.net/tests/dom/tests/mochitest/general/generateCss.sjs?N"/>
|
||||
|
||||
|
||||
<!-- 6. background-image: url()
|
||||
[r] http://example.net , generateCss.sjs?O
|
||||
[!] http://example.net , red.png
|
||||
-->
|
||||
<link rel="stylesheet" type="text/css" href="http://example.net/tests/dom/tests/mochitest/general/generateCss.sjs?O"/>
|
||||
|
||||
|
||||
<!-- 7. @font-face
|
||||
[r] http://example.net , generateCss.sjs?P
|
||||
[!] http://example.net , Ahem.tff
|
||||
-->
|
||||
<link rel="stylesheet" type="text/css" href="http://example.net/tests/dom/tests/mochitest/general/generateCss.sjs?P"/>
|
||||
|
||||
|
||||
<!-- 8. cursor: url()
|
||||
[r] http://example.net , generateCss.sjs?Q
|
||||
[!] http://example.net , over.png
|
||||
-->
|
||||
<link rel="stylesheet" type="text/css" href="http://example.net/tests/dom/tests/mochitest/general/generateCss.sjs?Q"/>
|
||||
|
||||
|
||||
<!-- 9. mask: url(res.svg#mask)
|
||||
TODO: I don't think this is working properly. Must fix.
|
||||
[r] http://example.net , generateCss.sjs?R
|
||||
[!] http://example.net , file_use_counter_svg_fill_pattern_data.svg
|
||||
-->
|
||||
<link rel="stylesheet" type="text/css" href="http://example.net/tests/dom/tests/mochitest/general/generateCss.sjs?R"/>
|
||||
|
||||
|
||||
<!-- TODO: add test that we _do_ include subresources if the cross-origin sheet was fetched via CORS -->
|
||||
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function ok(cond, message) {
|
||||
window.opener.ok(cond, message)
|
||||
}
|
||||
|
||||
function is(received, expected, message) {
|
||||
window.opener.is(received, expected, message);
|
||||
}
|
||||
|
||||
function isnot(received, notExpected, message) {
|
||||
window.opener.isnot(received, notExpected, message);
|
||||
}
|
||||
|
||||
var allResources = {
|
||||
"http://mochi.test:8888/tests/SimpleTest/test.css" : "link",
|
||||
|
||||
// 1
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/general/generateCss.sjs?A" : "link",
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/general/generateCss.sjs?B" : "css",
|
||||
|
||||
// 2
|
||||
"http://example.com/tests/dom/tests/mochitest/general/generateCss.sjs?C" : "link",
|
||||
|
||||
// 3
|
||||
"http://example.com/tests/dom/tests/mochitest/general/generateCss.sjs?E" : "link",
|
||||
|
||||
// 4
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/general/generateCss.sjs?G" : "link",
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/general/generateCss.sjs?H" : "css",
|
||||
"http://example.com/tests/dom/tests/mochitest/general/generateCss.sjs?I" : "css",
|
||||
"http://example.org/tests/dom/tests/mochitest/general/generateCss.sjs?K" : "css",
|
||||
|
||||
// 5
|
||||
"http://example.net/tests/dom/tests/mochitest/general/generateCss.sjs?N" : "link",
|
||||
|
||||
// 6
|
||||
"http://example.net/tests/dom/tests/mochitest/general/generateCss.sjs?O" : "link",
|
||||
|
||||
// 7
|
||||
"http://example.net/tests/dom/tests/mochitest/general/generateCss.sjs?P" : "link",
|
||||
|
||||
// 8
|
||||
"http://example.net/tests/dom/tests/mochitest/general/generateCss.sjs?Q" : "link",
|
||||
|
||||
// 9
|
||||
"http://example.net/tests/dom/tests/mochitest/general/generateCss.sjs?R" : "link",
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
let entries = performance.getEntries();
|
||||
// The entries.slice() to drop first 'document' item.
|
||||
for (let entry of entries.slice(1)) {
|
||||
//dump(entry.name + " || "+ entry.initiatorType+ "\n");
|
||||
if (!(entry.name in allResources)) {
|
||||
if (entry.name.substr(-4) == ".ttf") {
|
||||
// TODO: fix hiding of font files
|
||||
continue;
|
||||
}
|
||||
ok(false, "Did not expect timing for resource: " + entry.name);
|
||||
continue;
|
||||
}
|
||||
let resType = allResources[entry.name];
|
||||
is(entry.initiatorType, resType,
|
||||
"Expected matching initiatorType for: " + entry.name);
|
||||
delete allResources[entry.name];
|
||||
}
|
||||
|
||||
for (let res in allResources) {
|
||||
ok(false, "Expect timing for resource: " + res);
|
||||
}
|
||||
|
||||
window.opener.finishTests();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1180145"
|
||||
title="Resource timing NO-CORS CSS">
|
||||
Bug #1180145 - Resource Timing NO-CORS CSS
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
</div>
|
||||
|
||||
<div class="c1"> BLUE </div>
|
||||
<div class="c2"> RED </div>
|
||||
<div class="c3"> Font </div>
|
||||
<div class="c4"> CURSOR </div>
|
||||
<div class="c5"> <img id="image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg=="> </div>
|
||||
<div class="c6"> </div>
|
||||
</body>
|
||||
</html>
|
42
dom/tests/mochitest/general/generateCss.sjs
Normal file
42
dom/tests/mochitest/general/generateCss.sjs
Normal file
@ -0,0 +1,42 @@
|
||||
function handleRequest(request, response) {
|
||||
response.setHeader("Content-Type", "text/css", false);
|
||||
response.write(gResponses[request.queryString]);
|
||||
}
|
||||
|
||||
let gResponses = {
|
||||
// 1
|
||||
"A": "@import 'generateCss.sjs?B';",
|
||||
"B": "",
|
||||
|
||||
// 2
|
||||
"C": "@import 'generateCss.sjs?D';",
|
||||
"D": "",
|
||||
|
||||
// 3
|
||||
"E": "@import 'generateCss.sjs?F';",
|
||||
"F": "",
|
||||
|
||||
// 4
|
||||
"G": "@import 'generateCss.sjs?H'; @import 'http://example.org/tests/dom/tests/mochitest/general/generateCss.sjs?K';",
|
||||
"H": "@import 'http://example.com/tests/dom/tests/mochitest/general/generateCss.sjs?I';",
|
||||
"I": "@import 'generateCss.sjs?J",
|
||||
"J": "",
|
||||
"K": "@import 'generateCss.sjs?L';",
|
||||
"L": "@import 'generateCss.sjs?M",
|
||||
"M": "",
|
||||
|
||||
// 5
|
||||
"N": ".c1 { background-image: -moz-image-rect(url('/image/test/mochitest/blue.png'), 0, 0, 200, 200);}",
|
||||
|
||||
// 6
|
||||
"O": ".c2 { background-image: url('/image/test/mochitest/red.png');}",
|
||||
|
||||
// 7
|
||||
"P": "@font-face { font-family: Ahem; src: url('/tests/dom/base/test/Ahem.ttf'); } .c3 { font-family: Ahem; font-size: 20px; }",
|
||||
|
||||
// 8
|
||||
"Q": ".c4 { cursor: url('/image/test/mochitest/over.png') 2 2, auto; } ",
|
||||
|
||||
// 9
|
||||
"R": "#image { mask: url('/tests/dom/base/test/file_use_counter_svg_fill_pattern_data.svg'); }",
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
@import 'emptyCssFile2.css';
|
||||
@import url('http://example.org/tests/dom/tests/mochitest/general/emptyCssFile2.css');
|
||||
|
@ -9,6 +9,8 @@ support-files =
|
||||
file_interfaces.xml
|
||||
file_moving_nodeList.html
|
||||
file_moving_xhr.html
|
||||
file_resource_timing_nocors.html
|
||||
generateCss.sjs
|
||||
historyframes.html
|
||||
start_historyframe.html
|
||||
url1_historyframe.html
|
||||
@ -56,7 +58,9 @@ support-files =
|
||||
!/image/test/mochitest/damon.jpg
|
||||
!/image/test/mochitest/over.png
|
||||
!/image/test/mochitest/red.png
|
||||
!/dom/base/test/Ahem.ttf
|
||||
!/dom/base/test/file_empty.html
|
||||
!/dom/base/test/file_use_counter_svg_fill_pattern_data.svg
|
||||
file_focusrings.html
|
||||
|
||||
[test_497898.html]
|
||||
@ -135,5 +139,6 @@ skip-if = toolkit == 'android' # bug 1230232 - Mouse doesn't select in the same
|
||||
[test_WebKitCSSMatrix.html]
|
||||
[test_windowedhistoryframes.html]
|
||||
[test_windowProperties.html]
|
||||
[test_resource_timing_nocors.html]
|
||||
[test_resizeby.html]
|
||||
skip-if = toolkit == 'android' # Window sizes cannot be controled on android.
|
||||
|
@ -66,6 +66,13 @@ function isnot(received, notExpected, message) {
|
||||
var bufferFullCounter = 0;
|
||||
const expectedBufferFullEvents = 1;
|
||||
|
||||
var allResources = {
|
||||
"http://mochi.test:8888/tests/SimpleTest/test.css": "link",
|
||||
"http://mochi.test:8888/tests/image/test/mochitest/blue.png" : "img",
|
||||
"http://mochi.test:8888/tests/image/test/mochitest/red.png" : "object",
|
||||
"http://mochi.test:8888/tests/image/test/mochitest/big.png" : "embed",
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html" : "iframe"};
|
||||
|
||||
window.onload = function() {
|
||||
ok(!!window.performance, "Performance object should exist");
|
||||
ok(!!window.performance.getEntries, "Performance.getEntries() should exist");
|
||||
@ -76,8 +83,7 @@ window.onload = function() {
|
||||
bufferFullCounter += 1;
|
||||
}
|
||||
|
||||
// Here, we should have 5 entries (1 css, 3 png, 1 html) since the image was loaded.
|
||||
is(window.performance.getEntriesByType("resource").length, 5, "Performance.getEntriesByType() returned wrong number of entries.");
|
||||
is(window.performance.getEntriesByType("resource").length, Object.keys(allResources).length, "Performance.getEntriesByType() returned wrong number of entries.");
|
||||
|
||||
checkStringify(window.performance.getEntriesByType("resource")[0]);
|
||||
|
||||
@ -106,11 +112,11 @@ window.onload = function() {
|
||||
checkEntries(window.performance.getEntriesByType("resource"));
|
||||
|
||||
window.performance.setResourceTimingBufferSize(1);
|
||||
is(window.performance.getEntriesByType("resource").length, 5, "No entries should be " +
|
||||
is(window.performance.getEntriesByType("resource").length, Object.keys(allResources).length, "No entries should be " +
|
||||
"removed when setResourceTimingBufferSize is called.");
|
||||
|
||||
window.performance.setResourceTimingBufferSize(4);
|
||||
is(window.performance.getEntriesByType("resource").length, 5, "No entries should be " +
|
||||
is(window.performance.getEntriesByType("resource").length, Object.keys(allResources).length, "No entries should be " +
|
||||
"removed when setResourceTimingBufferSize is called.");
|
||||
|
||||
window.performance.setResourceTimingBufferSize(1);
|
||||
@ -183,13 +189,6 @@ function checkEntries(anEntryList) {
|
||||
|
||||
// Check that the entries have the expected initiator type. We can't check
|
||||
// the order (the order might depend on the platform the tests are running).
|
||||
allResources = {
|
||||
"http://mochi.test:8888/tests/SimpleTest/test.css" : "link",
|
||||
"http://mochi.test:8888/tests/image/test/mochitest/blue.png" : "img",
|
||||
"http://mochi.test:8888/tests/image/test/mochitest/red.png" : "object",
|
||||
"http://mochi.test:8888/tests/image/test/mochitest/big.png" : "embed",
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html" : "iframe"};
|
||||
|
||||
for (resourceName in allResources) {
|
||||
// Check that we have a resource with the specific name.
|
||||
namedEntries = window.performance.getEntriesByName(resourceName);
|
||||
|
88
dom/tests/mochitest/general/resource_timing_nocors.html
Normal file
88
dom/tests/mochitest/general/resource_timing_nocors.html
Normal file
@ -0,0 +1,88 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/dom/tests/mochitest/general/linkA.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="http://example.com/tests/dom/tests/mochitest/general/linkB.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="http://example.net/tests/dom/tests/mochitest/general/linkC.css"/>
|
||||
|
||||
<!--
|
||||
|
||||
Resources fetched by a cross-origin stylesheet loaded with a no-cors mode should not be reported.
|
||||
Resources marked with a ^ should be reported in performance.getEntries()
|
||||
|
||||
(mochi.test:8888 | linkA.css)^ -> (mochi.test:8888 | cssA.css)^
|
||||
-> (mochi.test:8888 | cssB.css)^ -> (mochi.test:8888 | cssC.css)^
|
||||
-> (example.org | cssC.css)^
|
||||
(example.com | linkB.css)^ -> (example.com | cssA.css)
|
||||
-> (mochi.test:8888 | cssA.css)
|
||||
-> (test2.examp.org | cssB.css) -> (test2.examp.org | cssC.css)
|
||||
-> (example.org | cssC.css)
|
||||
-> (example.net | cssC.css)
|
||||
|
||||
(example.net | linkC.css)^ -> (example.net | cssA.css)
|
||||
[WITH Allow-* HEADERS]
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function ok(cond, message) {
|
||||
window.opener.ok(cond, message)
|
||||
}
|
||||
|
||||
function is(received, expected, message) {
|
||||
window.opener.is(received, expected, message);
|
||||
}
|
||||
|
||||
function isnot(received, notExpected, message) {
|
||||
window.opener.isnot(received, notExpected, message);
|
||||
}
|
||||
|
||||
var allResources = {
|
||||
"http://mochi.test:8888/tests/SimpleTest/test.css" : "link",
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/general/linkA.css" : "link",
|
||||
"http://example.com/tests/dom/tests/mochitest/general/linkB.css" : "link",
|
||||
"http://example.net/tests/dom/tests/mochitest/general/linkC.css" : "link",
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/general/cssA.css" : "css",
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/general/cssB.css" : "css",
|
||||
"http://mochi.test:8888/tests/dom/tests/mochitest/general/cssC.css" : "css",
|
||||
"http://example.org/tests/dom/tests/mochitest/general/cssC.css" : "css",
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
let entries = performance.getEntries();
|
||||
for (let entry of entries) {
|
||||
let type = allResources[entry.name];
|
||||
if (!type) {
|
||||
ok(false, "Did not expect to find resource: "+entry.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
is(entry.initiatorType, type, "Expected initiatorType does not match");
|
||||
}
|
||||
|
||||
is(entries.length, Object.keys(allResources).length, "Found wrong number of resources");
|
||||
|
||||
window.opener.finishTests();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1180145"
|
||||
title="Resource timing NO-CORS CSS">
|
||||
Bug #1180145 - Resource Timing NO-CORS CSS
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -238,7 +238,7 @@ const gTests = {
|
||||
let mql = window.matchMedia(`(resolution: ${dppx}dppx)`);
|
||||
|
||||
mql.addListener(function listener() {
|
||||
ok("MediaQueryList's listener invoked.")
|
||||
ok(true, "MediaQueryList's listener invoked.")
|
||||
mql.removeListener(listener);
|
||||
resolve();
|
||||
});
|
||||
@ -247,7 +247,7 @@ const gTests = {
|
||||
let mql = frameWindow.matchMedia(`(resolution: ${dppx}dppx)`);
|
||||
|
||||
mql.addListener(function listener() {
|
||||
ok("frame's MediaQueryList's listener invoked.")
|
||||
ok(true, "frame's MediaQueryList's listener invoked.")
|
||||
mql.removeListener(listener);
|
||||
resolve();
|
||||
});
|
||||
@ -270,7 +270,7 @@ const gTests = {
|
||||
let mql = window.matchMedia(`(resolution: ${dppx}dppx)`);
|
||||
|
||||
mql.addListener(function listener() {
|
||||
ok("MediaQueryList's listener for dppx invoked.");
|
||||
ok(true, "MediaQueryList's listener for dppx invoked.");
|
||||
mql.removeListener(listener);
|
||||
overridden = true;
|
||||
resolve();
|
||||
@ -289,14 +289,14 @@ const gTests = {
|
||||
})
|
||||
];
|
||||
|
||||
setOverrideDPPX(dppx);
|
||||
setFullZoom(zoom);
|
||||
|
||||
promises[0]
|
||||
.then(() => setOverrideDPPX(0))
|
||||
.then(promises[1])
|
||||
.then(() => setFullZoom(originalZoom))
|
||||
.then(done, e => {throw e});
|
||||
|
||||
setOverrideDPPX(dppx);
|
||||
setFullZoom(zoom);
|
||||
},
|
||||
"test OverrideDPPX is kept on document navigation": (done) => {
|
||||
assertValuesAreInitial();
|
||||
|
37
dom/tests/mochitest/general/test_resource_timing_nocors.html
Normal file
37
dom/tests/mochitest/general/test_resource_timing_nocors.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1180145
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.enable_resource_timing", true]]}, start);
|
||||
var subwindow = null;
|
||||
|
||||
function start() {
|
||||
subwindow = window.open("file_resource_timing_nocors.html");
|
||||
}
|
||||
|
||||
function finishTests() {
|
||||
subwindow.close();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
128
dom/u2f/U2F.cpp
128
dom/u2f/U2F.cpp
@ -5,7 +5,6 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/U2F.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/WebCryptoCommon.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
@ -33,7 +32,6 @@ namespace dom {
|
||||
|
||||
static mozilla::LazyLogModule gU2FLog("u2fmanager");
|
||||
|
||||
NS_NAMED_LITERAL_STRING(kVisibilityChange, "visibilitychange");
|
||||
NS_NAMED_LITERAL_STRING(kFinishEnrollment, "navigator.id.finishEnrollment");
|
||||
NS_NAMED_LITERAL_STRING(kGetAssertion, "navigator.id.getAssertion");
|
||||
|
||||
@ -160,6 +158,23 @@ EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
|
||||
return ErrorCode::BAD_REQUEST;
|
||||
}
|
||||
|
||||
nsAutoCString appIdHost;
|
||||
if (NS_FAILED(appIdUri->GetAsciiHost(appIdHost))) {
|
||||
return ErrorCode::BAD_REQUEST;
|
||||
}
|
||||
|
||||
// Allow localhost.
|
||||
if (appIdHost.EqualsLiteral("localhost")) {
|
||||
nsAutoCString facetHost;
|
||||
if (NS_FAILED(facetUri->GetAsciiHost(facetHost))) {
|
||||
return ErrorCode::BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (facetHost.EqualsLiteral("localhost")) {
|
||||
return ErrorCode::OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Run the HTML5 algorithm to relax the same-origin policy, copied from W3C
|
||||
// Web Authentication. See Bug 1244959 comment #8 for context on why we are
|
||||
// doing this instead of implementing the external-fetch FacetID logic.
|
||||
@ -184,10 +199,6 @@ EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
|
||||
if (NS_FAILED(tldService->GetBaseDomain(facetUri, 0, lowestFacetHost))) {
|
||||
return ErrorCode::BAD_REQUEST;
|
||||
}
|
||||
nsAutoCString appIdHost;
|
||||
if (NS_FAILED(appIdUri->GetAsciiHost(appIdHost))) {
|
||||
return ErrorCode::BAD_REQUEST;
|
||||
}
|
||||
|
||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
||||
("AppId %s Facet %s", appIdHost.get(), lowestFacetHost.get()));
|
||||
@ -278,12 +289,6 @@ ExecuteCallback(T& aResp, Maybe<nsMainThreadPtrHandle<C>>& aCb)
|
||||
* U2F JavaScript API Implementation
|
||||
**********************************************************************/
|
||||
|
||||
U2F::U2F(nsPIDOMWindowInner* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
U2F::~U2F()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@ -671,104 +676,5 @@ U2F::RequestAborted(const uint64_t& aTransactionId, const nsresult& aError)
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Event Handling
|
||||
**********************************************************************/
|
||||
|
||||
void
|
||||
U2F::ListenForVisibilityEvents()
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = doc->AddSystemEventListener(kVisibilityChange, this,
|
||||
/* use capture */ true,
|
||||
/* wants untrusted */ false);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
void
|
||||
U2F::StopListeningForVisibilityEvents()
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = doc->RemoveSystemEventListener(kVisibilityChange, this,
|
||||
/* use capture */ true);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
U2F::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aEvent);
|
||||
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
if (!type.Equals(kVisibilityChange)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc =
|
||||
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (doc->Hidden()) {
|
||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
||||
("Visibility change: U2F window is hidden, cancelling job."));
|
||||
|
||||
CancelTransaction(NS_ERROR_ABORT);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* IPC Protocol Implementation
|
||||
**********************************************************************/
|
||||
|
||||
bool
|
||||
U2F::MaybeCreateBackgroundActor()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mChild) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
|
||||
if (NS_WARN_IF(!actorChild)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<WebAuthnTransactionChild> mgr(new WebAuthnTransactionChild(this));
|
||||
PWebAuthnTransactionChild* constructedMgr =
|
||||
actorChild->SendPWebAuthnTransactionConstructor(mgr);
|
||||
|
||||
if (NS_WARN_IF(!constructedMgr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(constructedMgr == mgr);
|
||||
mChild = mgr.forget();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
U2F::ActorDestroyed()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mChild = nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -18,14 +18,10 @@
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "U2FAuthenticator.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
|
||||
class nsISerialEventTarget;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class WebAuthnTransactionChild;
|
||||
class U2FRegisterCallback;
|
||||
class U2FSignCallback;
|
||||
|
||||
@ -59,17 +55,16 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class U2F final : public nsIDOMEventListener
|
||||
, public WebAuthnManagerBase
|
||||
class U2F final : public WebAuthnManagerBase
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(U2F)
|
||||
|
||||
explicit U2F(nsPIDOMWindowInner* aParent);
|
||||
explicit U2F(nsPIDOMWindowInner* aParent)
|
||||
: WebAuthnManagerBase(aParent)
|
||||
{ }
|
||||
|
||||
nsPIDOMWindowInner*
|
||||
GetParentObject() const
|
||||
@ -114,35 +109,25 @@ public:
|
||||
RequestAborted(const uint64_t& aTransactionId,
|
||||
const nsresult& aError) override;
|
||||
|
||||
void ActorDestroyed() override;
|
||||
protected:
|
||||
// Cancels the current transaction (by sending a Cancel message to the
|
||||
// parent) and rejects it by calling RejectTransaction().
|
||||
void CancelTransaction(const nsresult& aError) override;
|
||||
|
||||
private:
|
||||
~U2F();
|
||||
|
||||
// Visibility event handling.
|
||||
void ListenForVisibilityEvents();
|
||||
void StopListeningForVisibilityEvents();
|
||||
|
||||
// Clears all information we have about the current transaction.
|
||||
void ClearTransaction();
|
||||
// Rejects the current transaction and calls ClearTransaction().
|
||||
void RejectTransaction(const nsresult& aError);
|
||||
// Cancels the current transaction (by sending a Cancel message to the
|
||||
// parent) and rejects it by calling RejectTransaction().
|
||||
void CancelTransaction(const nsresult& aError);
|
||||
|
||||
bool MaybeCreateBackgroundActor();
|
||||
|
||||
nsString mOrigin;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
|
||||
// U2F API callbacks.
|
||||
Maybe<nsMainThreadPtrHandle<U2FRegisterCallback>> mRegisterCallback;
|
||||
Maybe<nsMainThreadPtrHandle<U2FSignCallback>> mSignCallback;
|
||||
|
||||
// IPC Channel to the parent process.
|
||||
RefPtr<WebAuthnTransactionChild> mChild;
|
||||
|
||||
// The current transaction, if any.
|
||||
Maybe<U2FTransaction> mTransaction;
|
||||
};
|
||||
|
@ -1,6 +1,8 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
tab_u2f_result.html
|
||||
skip-if = !e10s
|
||||
|
||||
[browser_abort_visibility.js]
|
||||
[browser_appid_localhost.js]
|
||||
|
@ -6,22 +6,6 @@
|
||||
|
||||
const TEST_URL = "https://example.com/browser/dom/u2f/tests/browser/tab_u2f_result.html";
|
||||
|
||||
function bytesToBase64(u8a){
|
||||
let CHUNK_SZ = 0x8000;
|
||||
let c = [];
|
||||
for (let i = 0; i < u8a.length; i += CHUNK_SZ) {
|
||||
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
|
||||
}
|
||||
return window.btoa(c.join(""));
|
||||
}
|
||||
|
||||
function bytesToBase64UrlSafe(buf) {
|
||||
return bytesToBase64(buf)
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=/g, "");
|
||||
}
|
||||
|
||||
async function assertStatus(tab, expected) {
|
||||
let actual = await ContentTask.spawn(tab.linkedBrowser, null, async function () {
|
||||
return content.document.getElementById("status").value;
|
||||
|
78
dom/u2f/tests/browser/browser_appid_localhost.js
Normal file
78
dom/u2f/tests/browser/browser_appid_localhost.js
Normal file
@ -0,0 +1,78 @@
|
||||
/* 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";
|
||||
|
||||
const TEST_URL = "https://localhost/";
|
||||
|
||||
function promiseU2FRegister(tab, app_id) {
|
||||
let challenge = crypto.getRandomValues(new Uint8Array(16));
|
||||
challenge = bytesToBase64UrlSafe(challenge);
|
||||
|
||||
return ContentTask.spawn(tab.linkedBrowser, [app_id, challenge], async function ([app_id, challenge]) {
|
||||
return new Promise(resolve => {
|
||||
let version = "U2F_V2";
|
||||
content.u2f.register(app_id, [{version, challenge}], [], resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function () {
|
||||
// Enable the soft token.
|
||||
Services.prefs.setBoolPref("security.webauth.u2f", true);
|
||||
Services.prefs.setBoolPref("security.webauth.webauthn_enable_softtoken", true);
|
||||
Services.prefs.setBoolPref("security.webauth.webauthn_enable_usbtoken", false);
|
||||
|
||||
// Open a new tab.
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
|
||||
// Check that we have the right origin, and U2F is available.
|
||||
let ready = await ContentTask.spawn(tab.linkedBrowser, null, async () => {
|
||||
return content.location.origin == "https://localhost" && content.u2f;
|
||||
});
|
||||
ok(ready, "Origin is https://localhost. U2F is available.");
|
||||
|
||||
// Test: Null AppID
|
||||
await promiseU2FRegister(tab, null).then(res => {
|
||||
is(res.errorCode, 0, "Null AppID should work.");
|
||||
});
|
||||
|
||||
// Test: Empty AppID
|
||||
await promiseU2FRegister(tab, "").then(res => {
|
||||
is(res.errorCode, 0, "Empty AppID should work.");
|
||||
});
|
||||
|
||||
// Test: Correct TLD, incorrect scheme
|
||||
await promiseU2FRegister(tab, "http://localhost/appId").then(res => {
|
||||
isnot(res.errorCode, 0, "Incorrect scheme.");
|
||||
});
|
||||
|
||||
// Test: Incorrect TLD
|
||||
await promiseU2FRegister(tab, "https://localhost.ssl/appId").then(res => {
|
||||
isnot(res.errorCode, 0, "Incorrect TLD.");
|
||||
});
|
||||
|
||||
// Test: Incorrect TLD
|
||||
await promiseU2FRegister(tab, "https://sub.localhost/appId").then(res => {
|
||||
isnot(res.errorCode, 0, "Incorrect TLD.");
|
||||
});
|
||||
|
||||
// Test: Correct TLD
|
||||
await promiseU2FRegister(tab, "https://localhost/appId").then(res => {
|
||||
is(res.errorCode, 0, "https://localhost/appId should work.");
|
||||
});
|
||||
|
||||
// Test: Correct TLD
|
||||
await promiseU2FRegister(tab, "https://localhost:443/appId").then(res => {
|
||||
is(res.errorCode, 0, "https://localhost:443/appId should work.");
|
||||
});
|
||||
|
||||
// Close tab.
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
|
||||
// Cleanup.
|
||||
Services.prefs.clearUserPref("security.webauth.u2f");
|
||||
Services.prefs.clearUserPref("security.webauth.webauthn_enable_softtoken");
|
||||
Services.prefs.clearUserPref("security.webauth.webauthn_enable_usbtoken");
|
||||
});
|
21
dom/u2f/tests/browser/head.js
Normal file
21
dom/u2f/tests/browser/head.js
Normal file
@ -0,0 +1,21 @@
|
||||
/* 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";
|
||||
|
||||
function bytesToBase64(u8a){
|
||||
let CHUNK_SZ = 0x8000;
|
||||
let c = [];
|
||||
for (let i = 0; i < u8a.length; i += CHUNK_SZ) {
|
||||
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
|
||||
}
|
||||
return window.btoa(c.join(""));
|
||||
}
|
||||
|
||||
function bytesToBase64UrlSafe(buf) {
|
||||
return bytesToBase64(buf)
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=/g, "");
|
||||
}
|
@ -42,8 +42,6 @@ namespace {
|
||||
static mozilla::LazyLogModule gWebAuthnManagerLog("webauthnmanager");
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_STRING(kVisibilityChange, "visibilitychange");
|
||||
|
||||
NS_IMPL_ISUPPORTS(WebAuthnManager, nsIDOMEventListener);
|
||||
|
||||
/***********************************************************************
|
||||
@ -145,44 +143,10 @@ RelaxSameOrigin(nsPIDOMWindowInner* aParent,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::ListenForVisibilityEvents()
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = doc->AddSystemEventListener(kVisibilityChange, this,
|
||||
/* use capture */ true,
|
||||
/* wants untrusted */ false);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::StopListeningForVisibilityEvents()
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = doc->RemoveSystemEventListener(kVisibilityChange, this,
|
||||
/* use capture */ true);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* WebAuthnManager Implementation
|
||||
**********************************************************************/
|
||||
|
||||
WebAuthnManager::WebAuthnManager(nsPIDOMWindowInner* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aParent);
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::ClearTransaction()
|
||||
{
|
||||
@ -229,34 +193,6 @@ WebAuthnManager::~WebAuthnManager()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WebAuthnManager::MaybeCreateBackgroundActor()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mChild) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread();
|
||||
if (NS_WARN_IF(!actor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<WebAuthnTransactionChild> mgr(new WebAuthnTransactionChild(this));
|
||||
PWebAuthnTransactionChild* constructedMgr =
|
||||
actor->SendPWebAuthnTransactionConstructor(mgr);
|
||||
|
||||
if (NS_WARN_IF(!constructedMgr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(constructedMgr == mgr);
|
||||
mChild = mgr.forget();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WebAuthnManager::MakeCredential(const MakePublicKeyCredentialOptions& aOptions,
|
||||
const Optional<OwningNonNull<AbortSignal>>& aSignal)
|
||||
@ -888,46 +824,11 @@ WebAuthnManager::RequestAborted(const uint64_t& aTransactionId,
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnManager::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aEvent);
|
||||
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
if (!type.Equals(kVisibilityChange)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc =
|
||||
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (doc->Hidden()) {
|
||||
MOZ_LOG(gWebAuthnManagerLog, LogLevel::Debug,
|
||||
("Visibility change: WebAuthn window is hidden, cancelling job."));
|
||||
|
||||
CancelTransaction(NS_ERROR_ABORT);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::Abort()
|
||||
{
|
||||
CancelTransaction(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManager::ActorDestroyed()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mChild = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,8 @@
|
||||
#define mozilla_dom_WebAuthnManager_h
|
||||
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/PWebAuthnTransaction.h"
|
||||
#include "mozilla/dom/WebAuthnManagerBase.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
|
||||
/*
|
||||
* Content process manager for the WebAuthn protocol. Created on calls to the
|
||||
@ -53,14 +51,6 @@ public:
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct Account;
|
||||
class ArrayBufferViewOrArrayBuffer;
|
||||
struct AssertionOptions;
|
||||
class OwningArrayBufferViewOrArrayBuffer;
|
||||
struct MakePublicKeyCredentialOptions;
|
||||
class Promise;
|
||||
class WebAuthnTransactionChild;
|
||||
|
||||
class WebAuthnTransaction
|
||||
{
|
||||
public:
|
||||
@ -103,14 +93,14 @@ private:
|
||||
};
|
||||
|
||||
class WebAuthnManager final : public WebAuthnManagerBase
|
||||
, public nsIDOMEventListener
|
||||
, public AbortFollower
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
explicit WebAuthnManager(nsPIDOMWindowInner* aParent);
|
||||
explicit WebAuthnManager(nsPIDOMWindowInner* aParent)
|
||||
: WebAuthnManagerBase(aParent)
|
||||
{ }
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MakeCredential(const MakePublicKeyCredentialOptions& aOptions,
|
||||
@ -138,34 +128,22 @@ public:
|
||||
RequestAborted(const uint64_t& aTransactionId,
|
||||
const nsresult& aError) override;
|
||||
|
||||
void ActorDestroyed() override;
|
||||
|
||||
// AbortFollower
|
||||
|
||||
void Abort() override;
|
||||
|
||||
protected:
|
||||
// Cancels the current transaction (by sending a Cancel message to the
|
||||
// parent) and rejects it by calling RejectTransaction().
|
||||
void CancelTransaction(const nsresult& aError) override;
|
||||
|
||||
private:
|
||||
virtual ~WebAuthnManager();
|
||||
|
||||
// Visibility event handling.
|
||||
void ListenForVisibilityEvents();
|
||||
void StopListeningForVisibilityEvents();
|
||||
|
||||
// Clears all information we have about the current transaction.
|
||||
void ClearTransaction();
|
||||
// Rejects the current transaction and calls ClearTransaction().
|
||||
void RejectTransaction(const nsresult& aError);
|
||||
// Cancels the current transaction (by sending a Cancel message to the
|
||||
// parent) and rejects it by calling RejectTransaction().
|
||||
void CancelTransaction(const nsresult& aError);
|
||||
|
||||
bool MaybeCreateBackgroundActor();
|
||||
|
||||
// The parent window.
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
|
||||
// IPC Channel to the parent process.
|
||||
RefPtr<WebAuthnTransactionChild> mChild;
|
||||
|
||||
// The current transaction, if any.
|
||||
Maybe<WebAuthnTransaction> mTransaction;
|
||||
|
128
dom/webauthn/WebAuthnManagerBase.cpp
Normal file
128
dom/webauthn/WebAuthnManagerBase.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/WebAuthnManagerBase.h"
|
||||
#include "mozilla/dom/WebAuthnTransactionChild.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_NAMED_LITERAL_STRING(kVisibilityChange, "visibilitychange");
|
||||
|
||||
WebAuthnManagerBase::WebAuthnManagerBase(nsPIDOMWindowInner* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aParent);
|
||||
}
|
||||
|
||||
WebAuthnManagerBase::~WebAuthnManagerBase()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* IPC Protocol Implementation
|
||||
**********************************************************************/
|
||||
|
||||
bool
|
||||
WebAuthnManagerBase::MaybeCreateBackgroundActor()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mChild) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
|
||||
if (NS_WARN_IF(!actorChild)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<WebAuthnTransactionChild> mgr(new WebAuthnTransactionChild(this));
|
||||
PWebAuthnTransactionChild* constructedMgr =
|
||||
actorChild->SendPWebAuthnTransactionConstructor(mgr);
|
||||
|
||||
if (NS_WARN_IF(!constructedMgr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(constructedMgr == mgr);
|
||||
mChild = mgr.forget();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManagerBase::ActorDestroyed()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mChild = nullptr;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Event Handling
|
||||
**********************************************************************/
|
||||
|
||||
void
|
||||
WebAuthnManagerBase::ListenForVisibilityEvents()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = doc->AddSystemEventListener(kVisibilityChange, this,
|
||||
/* use capture */ true,
|
||||
/* wants untrusted */ false);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnManagerBase::StopListeningForVisibilityEvents()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = doc->RemoveSystemEventListener(kVisibilityChange, this,
|
||||
/* use capture */ true);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnManagerBase::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aEvent);
|
||||
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
if (!type.Equals(kVisibilityChange)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc =
|
||||
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (doc->Hidden()) {
|
||||
CancelTransaction(NS_ERROR_ABORT);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@
|
||||
#ifndef mozilla_dom_WebAuthnManagerBase_h
|
||||
#define mozilla_dom_WebAuthnManagerBase_h
|
||||
|
||||
#include "nsIDOMEventListener.h"
|
||||
|
||||
/*
|
||||
* A base class used by WebAuthn and U2F implementations, providing shared
|
||||
* functionality and requiring an interface used by the IPC child actors.
|
||||
@ -15,9 +17,15 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class WebAuthnManagerBase
|
||||
class WebAuthnTransactionChild;
|
||||
|
||||
class WebAuthnManagerBase : public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
explicit WebAuthnManagerBase(nsPIDOMWindowInner* aParent);
|
||||
|
||||
virtual void
|
||||
FinishMakeCredential(const uint64_t& aTransactionId,
|
||||
nsTArray<uint8_t>& aRegBuffer) = 0;
|
||||
@ -31,7 +39,25 @@ public:
|
||||
RequestAborted(const uint64_t& aTransactionId,
|
||||
const nsresult& aError) = 0;
|
||||
|
||||
virtual void ActorDestroyed() = 0;
|
||||
void ActorDestroyed();
|
||||
|
||||
protected:
|
||||
~WebAuthnManagerBase();
|
||||
|
||||
// Needed by HandleEvent() to cancel transactions.
|
||||
virtual void CancelTransaction(const nsresult& aError) = 0;
|
||||
|
||||
// Visibility event handling.
|
||||
void ListenForVisibilityEvents();
|
||||
void StopListeningForVisibilityEvents();
|
||||
|
||||
bool MaybeCreateBackgroundActor();
|
||||
|
||||
// The parent window.
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
|
||||
// IPC Channel to the parent process.
|
||||
RefPtr<WebAuthnTransactionChild> mChild;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ UNIFIED_SOURCES += [
|
||||
'U2FTokenManager.cpp',
|
||||
'WebAuthnCBORUtil.cpp',
|
||||
'WebAuthnManager.cpp',
|
||||
'WebAuthnManagerBase.cpp',
|
||||
'WebAuthnTransactionChild.cpp',
|
||||
'WebAuthnTransactionParent.cpp',
|
||||
'WebAuthnUtil.cpp',
|
||||
|
@ -262,7 +262,7 @@ nsXBLBinding::UnbindAnonymousContent(nsIDocument* aDocument,
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLBinding::SetBoundElement(nsIContent* aElement)
|
||||
nsXBLBinding::SetBoundElement(Element* aElement)
|
||||
{
|
||||
mBoundElement = aElement;
|
||||
if (mNextBinding)
|
||||
@ -301,7 +301,7 @@ nsXBLBinding::GenerateAnonymousContent()
|
||||
"Someone forgot a script blocker");
|
||||
|
||||
// Fetch the content element for this binding.
|
||||
nsIContent* content =
|
||||
Element* content =
|
||||
mPrototypeBinding->GetImmediateChild(nsGkAtoms::content);
|
||||
|
||||
if (!content) {
|
||||
@ -417,8 +417,12 @@ nsXBLBinding::GenerateAnonymousContent()
|
||||
}
|
||||
|
||||
// Conserve space by wiping the attributes off the clone.
|
||||
//
|
||||
// FIXME(emilio): It'd be nice to make `mContent` a `RefPtr<Element>`, but
|
||||
// as of right now it can also be a ShadowRoot (we don't enter in this
|
||||
// codepath though). Move Shadow DOM outside XBL and then fix that.
|
||||
if (mContent)
|
||||
mContent->UnsetAttr(namespaceID, name, false);
|
||||
mContent->AsElement()->UnsetAttr(namespaceID, name, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,8 +64,8 @@ public:
|
||||
nsXBLBinding* GetBaseBinding() const { return mNextBinding; }
|
||||
void SetBaseBinding(nsXBLBinding *aBinding);
|
||||
|
||||
nsIContent* GetBoundElement() { return mBoundElement; }
|
||||
void SetBoundElement(nsIContent *aElement);
|
||||
mozilla::dom::Element* GetBoundElement() { return mBoundElement; }
|
||||
void SetBoundElement(mozilla::dom::Element* aElement);
|
||||
|
||||
/*
|
||||
* Does a lookup for a method or attribute provided by one of the bindings'
|
||||
@ -173,7 +173,7 @@ protected:
|
||||
nsCOMPtr<nsIContent> mContent; // Strong. Our anonymous content stays around with us.
|
||||
RefPtr<nsXBLBinding> mNextBinding; // Strong. The derived binding owns the base class bindings.
|
||||
|
||||
nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it.
|
||||
mozilla::dom::Element* mBoundElement; // [WEAK] We have a reference, but we don't own it.
|
||||
|
||||
// The <xbl:children> elements that we found in our <xbl:content> when we
|
||||
// processed this binding. The default insertion point has no includes
|
||||
|
@ -535,7 +535,8 @@ nsXBLContentSink::OnOpenContainer(const char16_t **aAtts,
|
||||
nsresult
|
||||
nsXBLContentSink::ConstructBinding(uint32_t aLineNumber)
|
||||
{
|
||||
nsCOMPtr<nsIContent> binding = GetCurrentContent();
|
||||
// This is only called from HandleStartElement, so it'd better be an element.
|
||||
RefPtr<Element> binding = GetCurrentContent()->AsElement();
|
||||
binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, mCurrentBindingID);
|
||||
NS_ConvertUTF16toUTF8 cid(mCurrentBindingID);
|
||||
|
||||
@ -880,13 +881,12 @@ nsXBLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXBLContentSink::AddAttributes(const char16_t** aAtts,
|
||||
nsIContent* aContent)
|
||||
nsXBLContentSink::AddAttributes(const char16_t** aAtts, Element* aElement)
|
||||
{
|
||||
if (aContent->IsXULElement())
|
||||
if (aElement->IsXULElement())
|
||||
return NS_OK; // Nothing to do, since the proto already has the attrs.
|
||||
|
||||
return nsXMLContentSink::AddAttributes(aAtts, aContent);
|
||||
return nsXMLContentSink::AddAttributes(aAtts, aElement);
|
||||
}
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
|
@ -92,8 +92,7 @@ protected:
|
||||
nsIContent** aResult, bool* aAppendContent,
|
||||
mozilla::dom::FromParser aFromParser) override;
|
||||
|
||||
nsresult AddAttributes(const char16_t** aAtts,
|
||||
nsIContent* aContent) override;
|
||||
nsresult AddAttributes(const char16_t** aAtts, Element* aElement) override;
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
nsresult AddAttributesToXULPrototype(const char16_t **aAtts,
|
||||
|
@ -62,8 +62,8 @@ using namespace mozilla::dom;
|
||||
class nsXBLAttributeEntry {
|
||||
public:
|
||||
nsXBLAttributeEntry(nsAtom* aSrcAtom, nsAtom* aDstAtom,
|
||||
int32_t aDstNameSpace, nsIContent* aContent)
|
||||
: mElement(aContent),
|
||||
int32_t aDstNameSpace, Element* aElement)
|
||||
: mElement(aElement),
|
||||
mSrcAttribute(aSrcAtom),
|
||||
mDstAttribute(aDstAtom),
|
||||
mDstNameSpace(aDstNameSpace),
|
||||
@ -77,13 +77,13 @@ public:
|
||||
nsAtom* GetDstAttribute() { return mDstAttribute; }
|
||||
int32_t GetDstNameSpace() { return mDstNameSpace; }
|
||||
|
||||
nsIContent* GetElement() { return mElement; }
|
||||
Element* GetElement() { return mElement; }
|
||||
|
||||
nsXBLAttributeEntry* GetNext() { return mNext; }
|
||||
void SetNext(nsXBLAttributeEntry* aEntry) { mNext = aEntry; }
|
||||
|
||||
protected:
|
||||
nsIContent* mElement;
|
||||
Element* mElement;
|
||||
|
||||
RefPtr<nsAtom> mSrcAttribute;
|
||||
RefPtr<nsAtom> mDstAttribute;
|
||||
@ -113,7 +113,7 @@ nsXBLPrototypeBinding::nsXBLPrototypeBinding()
|
||||
nsresult
|
||||
nsXBLPrototypeBinding::Init(const nsACString& aID,
|
||||
nsXBLDocumentInfo* aInfo,
|
||||
nsIContent* aElement,
|
||||
Element* aElement,
|
||||
bool aFirstBinding)
|
||||
{
|
||||
nsresult rv = aInfo->DocumentURI()->Clone(getter_AddRefs(mBindingURI));
|
||||
@ -180,8 +180,7 @@ nsXBLPrototypeBinding::Trace(const TraceCallbacks& aCallbacks, void *aClosure) c
|
||||
void
|
||||
nsXBLPrototypeBinding::Initialize()
|
||||
{
|
||||
nsIContent* content = GetImmediateChild(nsGkAtoms::content);
|
||||
if (content) {
|
||||
if (Element* content = GetImmediateChild(nsGkAtoms::content)) {
|
||||
ConstructAttributeTable(content);
|
||||
}
|
||||
}
|
||||
@ -207,7 +206,7 @@ nsXBLPrototypeBinding::SetBasePrototype(nsXBLPrototypeBinding* aBinding)
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLPrototypeBinding::SetBindingElement(nsIContent* aElement)
|
||||
nsXBLPrototypeBinding::SetBindingElement(Element* aElement)
|
||||
{
|
||||
mBinding = aElement;
|
||||
if (mBinding->AttrValueIs(kNameSpaceID_None, nsGkAtoms::inheritstyle,
|
||||
@ -323,7 +322,7 @@ void
|
||||
nsXBLPrototypeBinding::AttributeChanged(nsAtom* aAttribute,
|
||||
int32_t aNameSpaceID,
|
||||
bool aRemoveFlag,
|
||||
nsIContent* aChangedElement,
|
||||
Element* aChangedElement,
|
||||
nsIContent* aAnonymousContent,
|
||||
bool aNotify)
|
||||
{
|
||||
@ -339,13 +338,12 @@ nsXBLPrototypeBinding::AttributeChanged(nsAtom* aAttribute,
|
||||
return;
|
||||
|
||||
// Iterate over the elements in the array.
|
||||
nsCOMPtr<nsIContent> content = GetImmediateChild(nsGkAtoms::content);
|
||||
RefPtr<Element> content = GetImmediateChild(nsGkAtoms::content);
|
||||
while (xblAttr) {
|
||||
nsIContent* element = xblAttr->GetElement();
|
||||
Element* element = xblAttr->GetElement();
|
||||
|
||||
nsCOMPtr<nsIContent> realElement = LocateInstance(aChangedElement, content,
|
||||
aAnonymousContent,
|
||||
element);
|
||||
RefPtr<Element> realElement =
|
||||
LocateInstance(aChangedElement, content, aAnonymousContent, element);
|
||||
|
||||
if (realElement) {
|
||||
// Hold a strong reference here so that the atom doesn't go away during
|
||||
@ -436,14 +434,14 @@ nsXBLPrototypeBinding::ImplementsInterface(REFNSIID aIID) const
|
||||
|
||||
// Internal helpers ///////////////////////////////////////////////////////////////////////
|
||||
|
||||
nsIContent*
|
||||
Element*
|
||||
nsXBLPrototypeBinding::GetImmediateChild(nsAtom* aTag)
|
||||
{
|
||||
for (nsIContent* child = mBinding->GetFirstChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
if (child->NodeInfo()->Equals(aTag, kNameSpaceID_XBL)) {
|
||||
return child;
|
||||
return child->AsElement();
|
||||
}
|
||||
}
|
||||
|
||||
@ -461,18 +459,18 @@ nsXBLPrototypeBinding::InitClass(const nsString& aClassName,
|
||||
aClassName, this, aClassObject, aNew);
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsXBLPrototypeBinding::LocateInstance(nsIContent* aBoundElement,
|
||||
Element*
|
||||
nsXBLPrototypeBinding::LocateInstance(Element* aBoundElement,
|
||||
nsIContent* aTemplRoot,
|
||||
nsIContent* aCopyRoot,
|
||||
nsIContent* aTemplChild)
|
||||
Element* aTemplChild)
|
||||
{
|
||||
// XXX We will get in trouble if the binding instantiation deviates from the template
|
||||
// in the prototype.
|
||||
if (aTemplChild == aTemplRoot || !aTemplChild)
|
||||
return nullptr;
|
||||
|
||||
nsIContent* templParent = aTemplChild->GetParent();
|
||||
Element* templParent = aTemplChild->GetParentElement();
|
||||
|
||||
// We may be disconnected from our parent during cycle collection.
|
||||
if (!templParent)
|
||||
@ -485,11 +483,17 @@ nsXBLPrototypeBinding::LocateInstance(nsIContent* aBoundElement,
|
||||
if (!copyParent)
|
||||
return nullptr;
|
||||
|
||||
return copyParent->GetChildAt(templParent->IndexOf(aTemplChild));
|
||||
nsIContent* child = copyParent->GetChildAt(templParent->IndexOf(aTemplChild));
|
||||
if (child && child->IsElement()) {
|
||||
return child->AsElement();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLPrototypeBinding::SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent)
|
||||
nsXBLPrototypeBinding::SetInitialAttributes(
|
||||
Element* aBoundElement,
|
||||
nsIContent* aAnonymousContent)
|
||||
{
|
||||
if (!mAttributeTable) {
|
||||
return;
|
||||
@ -528,9 +532,9 @@ nsXBLPrototypeBinding::SetInitialAttributes(nsIContent* aBoundElement, nsIConten
|
||||
while (curr) {
|
||||
nsAtom* dst = curr->GetDstAttribute();
|
||||
int32_t dstNs = curr->GetDstNameSpace();
|
||||
nsIContent* element = curr->GetElement();
|
||||
Element* element = curr->GetElement();
|
||||
|
||||
nsIContent* realElement =
|
||||
Element* realElement =
|
||||
LocateInstance(aBoundElement, content,
|
||||
aAnonymousContent, element);
|
||||
|
||||
@ -595,7 +599,7 @@ nsXBLPrototypeBinding::EnsureAttributeTable()
|
||||
void
|
||||
nsXBLPrototypeBinding::AddToAttributeTable(int32_t aSourceNamespaceID, nsAtom* aSourceTag,
|
||||
int32_t aDestNamespaceID, nsAtom* aDestTag,
|
||||
nsIContent* aContent)
|
||||
Element* aElement)
|
||||
{
|
||||
InnerAttributeTable* attributesNS = mAttributeTable->Get(aSourceNamespaceID);
|
||||
if (!attributesNS) {
|
||||
@ -604,7 +608,7 @@ nsXBLPrototypeBinding::AddToAttributeTable(int32_t aSourceNamespaceID, nsAtom* a
|
||||
}
|
||||
|
||||
nsXBLAttributeEntry* xblAttr =
|
||||
new nsXBLAttributeEntry(aSourceTag, aDestTag, aDestNamespaceID, aContent);
|
||||
new nsXBLAttributeEntry(aSourceTag, aDestTag, aDestNamespaceID, aElement);
|
||||
|
||||
nsXBLAttributeEntry* entry = attributesNS->Get(aSourceTag);
|
||||
if (!entry) {
|
||||
@ -617,7 +621,7 @@ nsXBLPrototypeBinding::AddToAttributeTable(int32_t aSourceNamespaceID, nsAtom* a
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLPrototypeBinding::ConstructAttributeTable(nsIContent* aElement)
|
||||
nsXBLPrototypeBinding::ConstructAttributeTable(Element* aElement)
|
||||
{
|
||||
// Don't add entries for <children> elements, since those will get
|
||||
// removed from the DOM when we construct the insertion point table.
|
||||
@ -692,7 +696,9 @@ nsXBLPrototypeBinding::ConstructAttributeTable(nsIContent* aElement)
|
||||
for (nsIContent* child = aElement->GetFirstChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
ConstructAttributeTable(child);
|
||||
if (child->IsElement()) {
|
||||
ConstructAttributeTable(child->AsElement());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1195,12 +1201,11 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
|
||||
if (namespaceID == XBLBinding_Serialize_NoContent)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIContent> content;
|
||||
|
||||
// If this is a text type, just read the string and return.
|
||||
if (namespaceID == XBLBinding_Serialize_TextNode ||
|
||||
namespaceID == XBLBinding_Serialize_CDATANode ||
|
||||
namespaceID == XBLBinding_Serialize_CommentNode) {
|
||||
nsCOMPtr<nsIContent> content;
|
||||
switch (namespaceID) {
|
||||
case XBLBinding_Serialize_TextNode:
|
||||
content = new nsTextNode(aNim);
|
||||
@ -1244,6 +1249,7 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
|
||||
rv = aStream->Read32(&attrCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
RefPtr<Element> element;
|
||||
// Create XUL prototype elements, or regular elements for other namespaces.
|
||||
// This needs to match the code in nsXBLContentSink::CreateElement.
|
||||
#ifdef MOZ_XUL
|
||||
@ -1293,17 +1299,12 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<Element> result;
|
||||
nsresult rv =
|
||||
nsXULElement::Create(prototype, aDocument, false, false, getter_AddRefs(result));
|
||||
nsXULElement::Create(prototype, aDocument, false, false, getter_AddRefs(element));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
content = result;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
#endif
|
||||
nsCOMPtr<Element> element;
|
||||
NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), NOT_FROM_PARSER);
|
||||
content = element;
|
||||
|
||||
for (uint32_t i = 0; i < attrCount; i++) {
|
||||
rv = ReadNamespace(aStream, namespaceID);
|
||||
@ -1322,7 +1323,7 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
|
||||
prefixAtom = NS_Atomize(prefix);
|
||||
|
||||
RefPtr<nsAtom> nameAtom = NS_Atomize(name);
|
||||
content->SetAttr(namespaceID, nameAtom, prefixAtom, val, false);
|
||||
element->SetAttr(namespaceID, nameAtom, prefixAtom, val, false);
|
||||
}
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
@ -1348,7 +1349,7 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
|
||||
RefPtr<nsAtom> destAtom = NS_Atomize(destAttribute);
|
||||
|
||||
EnsureAttributeTable();
|
||||
AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content);
|
||||
AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, element);
|
||||
|
||||
rv = ReadNamespace(aStream, srcNamespaceID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -1365,11 +1366,11 @@ nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
|
||||
|
||||
// Child may be null if this was a comment for example and can just be ignored.
|
||||
if (child) {
|
||||
content->AppendChildTo(child, false);
|
||||
element->AppendChildTo(child, false);
|
||||
}
|
||||
}
|
||||
|
||||
content.swap(*aContent);
|
||||
element.forget(aContent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1405,21 +1406,22 @@ nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
|
||||
}
|
||||
|
||||
// Otherwise, this is an element.
|
||||
Element* element = aNode->AsElement();
|
||||
|
||||
// Write the namespace id followed by the tag name
|
||||
rv = WriteNamespace(aStream, aNode->GetNameSpaceID());
|
||||
rv = WriteNamespace(aStream, element->GetNameSpaceID());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString prefixStr;
|
||||
aNode->NodeInfo()->GetPrefix(prefixStr);
|
||||
element->NodeInfo()->GetPrefix(prefixStr);
|
||||
rv = aStream->WriteWStringZ(prefixStr.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aStream->WriteWStringZ(nsDependentAtomString(aNode->NodeInfo()->NameAtom()).get());
|
||||
rv = aStream->WriteWStringZ(nsDependentAtomString(element->NodeInfo()->NameAtom()).get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Write attributes
|
||||
uint32_t count = aNode->GetAttrCount();
|
||||
uint32_t count = element->GetAttrCount();
|
||||
rv = aStream->Write32(count);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -1428,7 +1430,7 @@ nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
|
||||
// Write out the namespace id, the namespace prefix, the local tag name,
|
||||
// and the value, in that order.
|
||||
|
||||
const BorrowedAttrInfo attrInfo = aNode->GetAttrInfoAt(i);
|
||||
const BorrowedAttrInfo attrInfo = element->GetAttrInfoAt(i);
|
||||
const nsAttrName* name = attrInfo.mName;
|
||||
|
||||
// XXXndeakin don't write out xbl:inherits?
|
||||
@ -1462,7 +1464,7 @@ nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
|
||||
nsXBLAttributeEntry* entry = iter2.UserData();
|
||||
|
||||
do {
|
||||
if (entry->GetElement() == aNode) {
|
||||
if (entry->GetElement() == element) {
|
||||
WriteNamespace(aStream, srcNamespace);
|
||||
aStream->WriteWStringZ(
|
||||
nsDependentAtomString(entry->GetSrcAttribute()).get());
|
||||
@ -1480,12 +1482,12 @@ nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Finally, write out the child nodes.
|
||||
count = aNode->GetChildCount();
|
||||
count = element->GetChildCount();
|
||||
rv = aStream->Write32(count);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
rv = WriteContentNode(aStream, aNode->GetChildAt(i));
|
||||
rv = WriteContentNode(aStream, element->GetChildAt(i));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user