merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-12-02 15:20:57 +01:00
commit 0f9a85616c
503 changed files with 26199 additions and 8428 deletions

View File

@ -230,7 +230,9 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
nsIContent* containerElm = containerNode->IsElement() ?
containerNode->AsElement() : nullptr;
nsIFrame::RenderedText text = textFrame->GetRenderedText();
nsIFrame::RenderedText text = textFrame->GetRenderedText(0,
UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
// Remove text accessible if rendered text is empty.
if (textAcc) {

View File

@ -1091,7 +1091,9 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
// Create accessible for visible text frames.
if (content->IsNodeOfType(nsINode::eTEXT)) {
nsIFrame::RenderedText text = frame->GetRenderedText();
nsIFrame::RenderedText text = frame->GetRenderedText(0,
UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
// Ignore not rendered text nodes and whitespace text nodes between table
// cells.
if (text.mString.IsEmpty() ||

View File

@ -139,7 +139,9 @@ nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent,
if (aContent->TextLength() > 0) {
nsIFrame *frame = aContent->GetPrimaryFrame();
if (frame) {
nsIFrame::RenderedText text = frame->GetRenderedText();
nsIFrame::RenderedText text = frame->GetRenderedText(0,
UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
aString->Append(text.mString);
} else {
// If aContent is an object that is display: none, we have no a frame.

View File

@ -394,7 +394,9 @@ Accessible::VisibilityState()
if (frame->GetType() == nsGkAtoms::textFrame &&
!(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
frame->GetRect().IsEmpty()) {
nsIFrame::RenderedText text = frame->GetRenderedText();
nsIFrame::RenderedText text = frame->GetRenderedText(0,
UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
if (text.mString.IsEmpty()) {
return states::INVISIBLE;
}

View File

@ -1985,7 +1985,8 @@ HyperTextAccessible::ContentToRenderedOffset(nsIFrame* aFrame, int32_t aContentO
"Call on primary frame only");
nsIFrame::RenderedText text = aFrame->GetRenderedText(aContentOffset,
aContentOffset + 1);
aContentOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
*aRenderedOffset = text.mOffsetWithinNodeRenderedText;
return NS_OK;
@ -2009,7 +2010,8 @@ HyperTextAccessible::RenderedToContentOffset(nsIFrame* aFrame, uint32_t aRendere
"Call on primary frame only");
nsIFrame::RenderedText text = aFrame->GetRenderedText(aRenderedOffset,
aRenderedOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_RENDERED_TEXT);
aRenderedOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_RENDERED_TEXT,
nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
*aContentOffset = text.mOffsetWithinNodeText;
return NS_OK;

View File

@ -113,7 +113,7 @@
'A esoteric weapon wielded by only the most ' +
'formidable warriors, for its unrelenting strict' +
' power is unfathomable.',
'• Lists of Programming Languages', 'Lisp',
'• Lists of Programming Languages', 'Lisp ',
'1. Scheme', '2. Racket', '3. Clojure',
'4. Standard Lisp', 'link-0', ' Lisp',
'checkbox-1-5', ' LeLisp', '• JavaScript',
@ -124,7 +124,7 @@
'5 8', 'gridcell4', 'Just an innocuous separator',
'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2',
'switch-1', 'This is a MathML formula', 'math-1',
'switch-1', 'This is a MathML formula ', 'math-1',
'with some text after.']);
queueTraversalSequence(gQueue, docAcc, TraversalRules.Landmark, null,

View File

@ -52,7 +52,7 @@
gQueue, docAcc, ObjectTraversalRule, null,
['Main Title', 'Lorem ipsum ',
'dolor', ' sit amet. Integer vitae urna leo, id ',
'semper', ' nulla.', 'Second Section Title',
'semper', ' nulla. ', 'Second Section Title',
'Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.',
'An ', 'embedded', ' document.', 'Hide me', 'Link 1', 'Link 2',
'Link 3', 'Hello', 'World']);
@ -90,7 +90,7 @@
gQueue, docAcc, ObjectTraversalRule,
getAccessible(doc.getElementById('paragraph-1')),
['Lorem ipsum ', 'dolor', ' sit amet. Integer vitae urna leo, id ',
'semper', ' nulla.']);
'semper', ' nulla. ']);
gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
NS_ERROR_INVALID_ARG));

View File

@ -16,8 +16,8 @@
function doTest()
{
var iframeDoc = [ getNode("iframe").contentDocument ];
testCharacterCount(iframeDoc, 13);
testText(iframeDoc, 0, 13, "outbodyinbody");
testCharacterCount(iframeDoc, 15);
testText(iframeDoc, 0, 15, "outbody inbody ");
SimpleTest.finish();
}

View File

@ -58,7 +58,7 @@
////////////////////////////////////////////////////////////////////////
// getTextAtOffset line boundary
testTextAtOffset(0, BOUNDARY_LINE_START, "line", 0, 4,
testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5,
"hypertext3", kOk, kOk, kOk);
// XXX: see bug 634202.

View File

@ -14,9 +14,9 @@
function doTest()
{
testTextAtOffset("line_test_1", BOUNDARY_LINE_START,
[[0, 5, "Line 1", 0, 6],
[6, 6, "", 6, 6],
[7, 13, "Line 3", 7, 13]]);
[[0, 6, "Line 1 ", 0, 7],
[7, 7, "", 7, 7],
[8, 15, "Line 3 ", 8, 15]]);
//////////////////////////////////////////////////////////////////////////
// __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
@ -114,10 +114,10 @@
[ [ 0, 3, "foo\n", 0, 4 ], [ 4, 4, "", 4, 4 ] ]);
//////////////////////////////////////////////////////////////////////////
// 'Hello world'
// 'Hello world ' (\n is rendered as space)
testTextAtOffset([ "ht_4" ], BOUNDARY_LINE_START,
[ [ 0, 11, "Hello world", 0, 11 ] ]);
[ [ 0, 12, "Hello world ", 0, 12 ] ]);
//////////////////////////////////////////////////////////////////////////
// list items

View File

@ -89,7 +89,7 @@
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color,
"background-color": gComputedStyle.backgroundColor};
testTextAttrs(ID, 27, attrs, defAttrs, 27, 49);
testTextAttrs(ID, 27, attrs, defAttrs, 27, 50);
//////////////////////////////////////////////////////////////////////////
// area4
@ -110,7 +110,7 @@
tempElem = tempElem.parentNode;
gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
attrs = {"color": gComputedStyle.color};
testTextAttrs(ID, 34, attrs, defAttrs, 33, 45);
testTextAttrs(ID, 34, attrs, defAttrs, 33, 46);
//////////////////////////////////////////////////////////////////////////
// area5: "Green!*!RedNormal"
@ -144,7 +144,7 @@
// Normal
attrs = {};
testTextAttrs(ID, 11, attrs, defAttrs, 11, 17);
testTextAttrs(ID, 11, attrs, defAttrs, 11, 18);
//////////////////////////////////////////////////////////////////////////
// area6 (CSS vertical-align property, refer to bug 445938 for details
@ -323,7 +323,7 @@
testTextAttrs(ID, 152, attrs, defAttrs, 151, 164);
attrs = {};
testTextAttrs(ID, 165, attrs, defAttrs, 164, 171);
testTextAttrs(ID, 165, attrs, defAttrs, 164, 172);
//////////////////////////////////////////////////////////////////////////
// area10, different single style spans in non-styled paragraph
@ -383,7 +383,7 @@
testTextAttrs(ID, 111, attrs, defAttrs, 110, 123);
attrs = {};
testTextAttrs(ID, 124, attrs, defAttrs, 123, 130);
testTextAttrs(ID, 124, attrs, defAttrs, 123, 131);
//////////////////////////////////////////////////////////////////////////
// area11, "font-weight" tests
@ -410,7 +410,7 @@
testTextAttrs(ID, 51, attrs, defAttrs, 51, 57);
attrs = { };
testTextAttrs(ID, 57, attrs, defAttrs, 57, 96);
testTextAttrs(ID, 57, attrs, defAttrs, 57, 97);
//////////////////////////////////////////////////////////////////////////
// test out of range offset
@ -487,7 +487,7 @@
testTextAttrs(ID, 27, attrs, defAttrs, 27, 31);
attrs = { };
testTextAttrs(ID, 31, attrs, defAttrs, 31, 44);
testTextAttrs(ID, 31, attrs, defAttrs, 31, 45);
}
//////////////////////////////////////////////////////////////////////////
@ -530,7 +530,7 @@
"text-line-through-style": "wavy",
"text-line-through-color": "rgb(0, 0, 0)",
};
testTextAttrs(ID, 39, attrs, defAttrs, 39, 43);
testTextAttrs(ID, 39, attrs, defAttrs, 39, 44);
//////////////////////////////////////////////////////////////////////////
// area18, "auto-generation text" tests
@ -560,7 +560,7 @@
testTextAttrs(ID, 11, attrs, defAttrs, 10, 17);
attrs = {};
testTextAttrs(ID, 18, attrs, defAttrs, 17, 27);
testTextAttrs(ID, 18, attrs, defAttrs, 17, 28);
//////////////////////////////////////////////////////////////////////////
// area20, "aOffset as -1 (Mozilla Bug 789621)" test

View File

@ -71,7 +71,7 @@
turnCaretBrowsing(true);
// test caret offsets
testCaretOffset(document, 15);
testCaretOffset(document, 16);
testCaretOffset("textbox", -1);
testCaretOffset("textarea", -1);
testCaretOffset("p", -1);

View File

@ -49,14 +49,14 @@
},
{
role: ROLE_TEXT_LEAF,
name: "Hello3"
name: "Hello3 "
},
{
role: ROLE_PARAGRAPH,
children: [
{
role: ROLE_TEXT_LEAF,
name: "Hello4"
name: "Hello4 "
}
]
}
@ -71,7 +71,7 @@
children: [
{
role: ROLE_TEXT_LEAF,
name: "helllo"
name: "helllo "
},
{
role: ROLE_PARAGRAPH,
@ -84,7 +84,7 @@
},
{
role: ROLE_TEXT_LEAF,
name: "hello"
name: "hello "
}
]
};

View File

@ -0,0 +1,12 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
BROWSER_CHROME_MANIFESTS += [
'test/browser/browser.ini',
]
with Files('**'):
BUG_COMPONENT = ('Firefox', 'Contextual Identity')

View File

@ -0,0 +1,7 @@
[DEFAULT]
skip-if = buildapp == "mulet"
support-files =
file_reflect_cookie_into_title.html
[browser_usercontext.js]
skip-if = e10s

View File

@ -0,0 +1,91 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const USER_CONTEXTS = [
"default",
"personal",
"work",
];
const BASE_URI = "http://mochi.test:8888/browser/browser/components/"
+ "contextualidentity/test/browser/file_reflect_cookie_into_title.html";
// opens `uri' in a new tab with the provided userContextId and focuses it.
// returns the newly opened tab
function openTabInUserContext(uri, userContextId) {
// open the tab in the correct userContextId
let tab = gBrowser.addTab(uri, {userContextId});
// select tab and make sure its browser is focused
gBrowser.selectedTab = tab;
tab.ownerDocument.defaultView.focus();
return tab;
}
add_task(function* setup() {
// make sure userContext is enabled.
SpecialPowers.pushPrefEnv({"set": [
["privacy.userContext.enabled", true]
]});
});
add_task(function* cleanup() {
// make sure we don't leave any prefs set for the next tests
registerCleanupFunction(function() {
SpecialPowers.popPrefEnv();
});
});
add_task(function* test() {
for (let userContextId of Object.keys(USER_CONTEXTS)) {
// load the page in 3 different contexts and set a cookie
// which should only be visible in that context
let cookie = USER_CONTEXTS[userContextId];
// open our tab in the given user context
let tab = openTabInUserContext(BASE_URI+"?"+cookie, userContextId);
// wait for tab load
yield BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab));
// remove the tab
gBrowser.removeTab(tab);
}
{
// Set a cookie in a different context so we can detect if that affects
// cross-context properly. If we don't do that, we get an UNEXPECTED-PASS
// for the localStorage case for the last tab we set.
let tab = openTabInUserContext(BASE_URI+"?foo", 9999);
yield BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab));
gBrowser.removeTab(tab);
}
for (let userContextId of Object.keys(USER_CONTEXTS)) {
// Load the page without setting the cookie this time
let expectedContext = USER_CONTEXTS[userContextId];
let tab = openTabInUserContext(BASE_URI, userContextId);
// wait for load
let browser = gBrowser.getBrowserForTab(tab);
yield BrowserTestUtils.browserLoaded(browser);
// get the title
let title = browser.contentDocument.title.trim().split("|");
// check each item in the title and validate it meets expectatations
for (let part of title) {
let [storageMethodName, value] = part.split("=");
let is_f = storageMethodName == "cookie" ? is : todo_is;
is_f(value, expectedContext,
"the title reflects the expected contextual identity of " +
expectedContext + " for method " + storageMethodName + ": " + value);
}
gBrowser.removeTab(tab);
}
});

View File

@ -0,0 +1,23 @@
<html>
<head>
<meta charset="UTF-8">
<title>title not set</title>
<script>
// if we have a query string, use it to set the cookie and localStorage
if (window.location.search.length > 0) {
let context_name = window.location.search.substr(1);
document.cookie = "userContextId=" + context_name;
localStorage.setItem("userContext", context_name);
}
// get the cookie
let [name, val] = document.cookie.split("=");
// set the title to reflect the cookie and local storage values we find
document.title = "cookie=" + val + "|"
+ "local=" + localStorage.getItem("userContext");
</script>
</head>
<body></body>
</html>

View File

@ -6,6 +6,7 @@
DIRS += [
'about',
'contextualidentity',
'customizableui',
'dirprovider',
'downloads',

View File

@ -228,6 +228,9 @@ l10n-check::
$(RM) -rf x-test
$(NSINSTALL) -D x-test/toolkit
echo '#define MOZ_LANG_TITLE Just testing' > x-test/toolkit/defines.inc
$(MAKE) installers-x-test L10NBASEDIR='$(PWD)' LOCALE_MERGEDIR='$(PWD)/mergedir'
@# ZIP_IN='$(ZIP_IN)' will pass down the *current* value of ZIP_IN, based
@# on MOZ_SIMPLE_PACKAGE_NAME not being reset, overwriting the value it
@# would get with MOZ_SIMPLE_PACKAGE_NAME reset.
$(MAKE) installers-x-test L10NBASEDIR='$(PWD)' LOCALE_MERGEDIR='$(PWD)/mergedir' ZIP_IN='$(ZIP_IN)' MOZ_SIMPLE_PACKAGE_NAME=
$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/unpack.py $(DIST)/l10n-stage/$(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)
cd $(DIST)/l10n-stage && test $$(cat $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/update.locale) = x-test

View File

@ -224,7 +224,33 @@ if test "$GNU_CXX"; then
elif test "$ac_cv_cxx0x_headers_bug" = "yes"; then
AC_MSG_ERROR([Your toolchain does not support C++0x/C++11 mode properly. Please upgrade your toolchain])
fi
AC_CACHE_CHECK([whether 64-bits std::atomic requires -latomic],
ac_cv_needs_atomic,
AC_TRY_LINK(
[#include <cstdint>
#include <atomic>],
[ std::atomic<uint64_t> foo; foo = 1; ],
ac_cv_needs_atomic=no,
_SAVE_LIBS="$LIBS"
LIBS="$LIBS -latomic"
AC_TRY_LINK(
[#include <cstdint>
#include <atomic>],
[ std::atomic<uint64_t> foo; foo = 1; ],
ac_cv_needs_atomic=yes,
ac_cv_needs_atomic="do not know; assuming no")
LIBS="$_SAVE_LIBS"
)
)
if test "$ac_cv_needs_atomic" = yes; then
MOZ_NEEDS_LIBATOMIC=1
else
MOZ_NEEDS_LIBATOMIC=
fi
AC_SUBST(MOZ_NEEDS_LIBATOMIC)
fi
if test -n "$CROSS_COMPILE"; then
dnl When cross compile, we have no variable telling us what the host compiler is. Figure it out.
cat > conftest.C <<EOF

View File

@ -65,6 +65,6 @@ private:
bool mFixupCreatedAlternateURI;
nsString mKeywordProviderName;
nsString mKeywordAsSent;
nsAutoCString mOriginalInput;
nsCString mOriginalInput;
};
#endif

View File

@ -13745,6 +13745,13 @@ nsDocShell::SetIsSignedPackage(const nsAString& aSignedPkg)
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetUserContextId(uint32_t aUserContextId)
{
mUserContextId = aUserContextId;
return NS_OK;
}
/* [infallible] */ NS_IMETHODIMP
nsDocShell::GetIsBrowserElement(bool* aIsBrowser)
{
@ -13854,6 +13861,8 @@ nsDocShell::GetOriginAttributes()
attrs.mAppId = mOwnOrContainingAppId;
}
attrs.mUserContextId = mUserContextId;
if (mFrameType == eFrameTypeBrowser) {
attrs.mInBrowser = true;
}

View File

@ -232,6 +232,7 @@ public:
NS_IMETHOD GetUseRemoteTabs(bool*) override;
NS_IMETHOD SetRemoteTabs(bool) override;
NS_IMETHOD GetOriginAttributes(JS::MutableHandle<JS::Value>) override;
NS_IMETHOD SetUserContextId(uint32_t);
// Restores a cached presentation from history (mLSHE).
// This method swaps out the content viewer and simulates loads for
@ -1002,6 +1003,9 @@ protected:
// find it by walking up the docshell hierarchy.)
uint32_t mOwnOrContainingAppId;
// userContextId signifying which container we are in
uint32_t mUserContextId;
nsString mPaymentRequestId;
nsString GetInheritedPaymentRequestId();

View File

@ -34,7 +34,7 @@ public:
}
private:
nsAutoString mRestyleHint;
nsString mRestyleHint;
};
} // namespace mozilla

View File

@ -14,6 +14,7 @@
#include "nsEscape.h"
#include "nsGkAtoms.h"
#include "nsHTMLDNSPrefetch.h"
#include "nsString.h"
#include "mozAutoDocUpdate.h"
@ -46,6 +47,31 @@ Link::ElementHasHref() const
mElement->HasAttr(kNameSpaceID_XLink, nsGkAtoms::href)));
}
void
Link::TryDNSPrefetch()
{
MOZ_ASSERT(mElement->IsInComposedDoc());
if (ElementHasHref() && nsHTMLDNSPrefetch::IsAllowed(mElement->OwnerDoc())) {
nsHTMLDNSPrefetch::PrefetchLow(this);
}
}
void
Link::CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag,
nsWrapperCache::FlagsType aRequestedFlag)
{
// If prefetch was deferred, clear flag and move on
if (mElement->HasFlag(aDeferredFlag)) {
mElement->UnsetFlags(aDeferredFlag);
// Else if prefetch was requested, clear flag and send cancellation
} else if (mElement->HasFlag(aRequestedFlag)) {
mElement->UnsetFlags(aRequestedFlag);
// Possible that hostname could have changed since binding, but since this
// covers common cases, most DNS prefetch requests will be canceled
nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT);
}
}
void
Link::SetLinkState(nsLinkState aState)
{

View File

@ -111,6 +111,11 @@ public:
bool ElementHasHref() const;
void TryDNSPrefetch();
void CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag,
nsWrapperCache::FlagsType aRequestedFlag);
protected:
virtual ~Link();

View File

@ -236,7 +236,7 @@ nsFrameLoader::LoadFrame()
if (NS_SUCCEEDED(rv)) {
rv = LoadURI(uri);
}
if (NS_FAILED(rv)) {
FireErrorEvent();
@ -319,7 +319,7 @@ nsFrameLoader::ReallyStartLoading()
if (NS_FAILED(rv)) {
FireErrorEvent();
}
return rv;
}
@ -339,7 +339,7 @@ nsFrameLoader::ReallyStartLoadingInternal()
// FIXME get error codes from child
mRemoteBrowser->LoadURL(mURIToLoad);
if (!mRemoteBrowserShown && !ShowRemoteFrame(ScreenIntSize(0, 0))) {
NS_WARNING("[nsFrameLoader] ReallyStartLoadingInternal tried but couldn't show remote browser.\n");
}
@ -372,7 +372,7 @@ nsFrameLoader::ReallyStartLoadingInternal()
loadInfo->SetOwner(mOwnerContent->NodePrincipal());
nsCOMPtr<nsIURI> referrer;
nsAutoString srcdoc;
bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc,
@ -546,7 +546,7 @@ nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem,
{
NS_PRECONDITION(aItem, "Must have docshell treeitem");
NS_PRECONDITION(mOwnerContent, "Must have owning content");
nsAutoString value;
bool isContent = false;
mOwnerContent->GetAttr(kNameSpaceID_None, TypeAttrName(), value);
@ -1127,7 +1127,7 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
!AllDescendantsOfType(otherDocshell, otherType))) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// Save off the tree owners, frame elements, chrome event handlers, and
// docshell and document parents before doing anything else.
nsCOMPtr<nsIDocShellTreeOwner> ourOwner, otherOwner;
@ -1237,7 +1237,7 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
ourOwner->ContentShellRemoved(ourDocshell);
otherOwner->ContentShellRemoved(otherDocshell);
}
ourParentItem->AddChild(otherDocshell);
otherParentItem->AddChild(ourDocshell);
@ -1788,6 +1788,23 @@ nsFrameLoader::MaybeCreateDocShell()
mDocShell->SetName(frameName);
}
//Grab the userContextId from owner if XUL
nsAutoString userContextIdStr;
if (namespaceID == kNameSpaceID_XUL) {
if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) {
mOwnerContent->GetAttr(kNameSpaceID_None,
nsGkAtoms::usercontextid,
userContextIdStr);
}
}
if (!userContextIdStr.IsEmpty()) {
nsresult err;
nsDocShell * ds = nsDocShell::Cast(mDocShell);
ds->SetUserContextId(userContextIdStr.ToInteger(&err));
NS_ENSURE_SUCCESS(err, err);
}
// Inform our docShell that it has a new child.
// Note: This logic duplicates a lot of logic in
// nsSubDocumentFrame::AttributeChanged. We should fix that.
@ -1961,7 +1978,7 @@ nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI)
NS_WARN_IF_FALSE(treeOwner,
"Trying to load a new url to a docshell without owner!");
NS_ENSURE_STATE(treeOwner);
if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) {
// No need to do recursion-protection here XXXbz why not?? Do we really
// trust people not to screw up with non-content docshells?
@ -1975,7 +1992,7 @@ nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI)
int32_t depth = 0;
while (parentAsItem) {
++depth;
if (depth >= MAX_DEPTH_CONTENT_FRAMES) {
mDepthTooGreat = true;
NS_WARNING("Too many nested content frames so giving up");
@ -2016,7 +2033,7 @@ nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI)
bool equal;
rv = aURI->EqualsExceptRef(parentURI, &equal);
NS_ENSURE_SUCCESS(rv, rv);
if (equal) {
matchCount++;
if (matchCount >= MAX_SAME_URL_CONTENT_FRAMES) {
@ -3064,6 +3081,23 @@ nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
// Populate packageId to signedPkg.
attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aPackageId);
// set the userContextId on the attrs before we pass them into
// the tab context
if (mOwnerContent) {
nsAutoString userContextIdStr;
if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) {
mOwnerContent->GetAttr(kNameSpaceID_None,
nsGkAtoms::usercontextid,
userContextIdStr);
}
if (!userContextIdStr.IsEmpty()) {
nsresult err;
uint32_t userContextId = userContextIdStr.ToInteger(&err);
NS_ENSURE_SUCCESS(err, err);
attrs.mUserContextId = userContextId;
}
}
bool tabContextUpdated =
aTabContext->SetTabContext(ownApp, containingApp, attrs, signedPkgOrigin);
NS_ENSURE_STATE(tabContextUpdated);

View File

@ -2405,3 +2405,6 @@ GK_ATOM(onboundary, "onboundary")
#endif
GK_ATOM(vr_state, "vr-state")
// Contextual Identity / Containers
GK_ATOM(usercontextid, "usercontextid")

View File

@ -14,7 +14,6 @@
#include "jsapi.h"
#include "jsfriendapi.h"
#include "xpcpublic.h"
#include "nsIUnicodeDecoder.h"
#include "nsIContent.h"
#include "nsJSUtils.h"
#include "mozilla/dom/ScriptSettings.h"
@ -54,7 +53,6 @@
#include "mozilla/Attributes.h"
#include "mozilla/unused.h"
#include "mozilla/dom/SRICheck.h"
#include "nsIScriptError.h"
using namespace mozilla;
@ -157,10 +155,10 @@ nsScriptLoader::~nsScriptLoader()
// subtree in the meantime and therefore aren't actually going away.
for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) {
mPendingChildLoaders[j]->RemoveExecuteBlocker();
}
}
}
NS_IMPL_ISUPPORTS(nsScriptLoader, nsIStreamLoaderObserver)
NS_IMPL_ISUPPORTS(nsScriptLoader, nsISupports)
// Helper method for checking if the script element is an event-handler
// This means that it has both a for-attribute and a event-attribute.
@ -269,37 +267,6 @@ nsScriptLoader::ShouldLoadScript(nsIDocument* aDocument,
return NS_OK;
}
class ContextMediator : public nsIStreamLoaderObserver
{
public:
explicit ContextMediator(nsScriptLoader *aScriptLoader, nsISupports *aContext)
: mScriptLoader(aScriptLoader)
, mContext(aContext) {}
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLOADEROBSERVER
private:
virtual ~ContextMediator() {}
RefPtr<nsScriptLoader> mScriptLoader;
nsCOMPtr<nsISupports> mContext;
};
NS_IMPL_ISUPPORTS(ContextMediator, nsIStreamLoaderObserver)
NS_IMETHODIMP
ContextMediator::OnStreamComplete(nsIStreamLoader* aLoader,
nsISupports* aContext,
nsresult aStatus,
uint32_t aStringLen,
const uint8_t* aString)
{
// pass arguments through except for the aContext,
// we have to mediate and use mContext instead.
return mScriptLoader->OnStreamComplete(aLoader, mContext, aStatus,
aStringLen, aString);
}
nsresult
nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
bool aScriptFromHead)
@ -383,10 +350,16 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
timedChannel->SetInitiatorType(NS_LITERAL_STRING("script"));
}
RefPtr<ContextMediator> mediator = new ContextMediator(this, aRequest);
nsAutoPtr<mozilla::dom::SRICheckDataVerifier> sriDataVerifier;
if (!aRequest->mIntegrity.IsEmpty()) {
sriDataVerifier = new SRICheckDataVerifier(aRequest->mIntegrity, mDocument);
}
nsCOMPtr<nsIStreamLoader> loader;
rv = NS_NewStreamLoader(getter_AddRefs(loader), mediator);
RefPtr<nsScriptLoadHandler> handler =
new nsScriptLoadHandler(this, aRequest, sriDataVerifier.forget());
nsCOMPtr<nsIIncrementalStreamLoader> loader;
rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), handler);
NS_ENSURE_SUCCESS(rv, rv);
return channel->AsyncOpen2(loader);
@ -1438,23 +1411,36 @@ nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
return rv;
}
NS_IMETHODIMP
nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
nsresult
nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
nsresult aStatus,
uint32_t aStringLen,
const uint8_t* aString)
nsresult aChannelStatus,
nsresult aSRIStatus,
mozilla::Vector<char16_t> &aString,
mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier)
{
nsScriptLoadRequest* request = static_cast<nsScriptLoadRequest*>(aContext);
NS_ASSERTION(request, "null request in stream complete handler");
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsresult rv = NS_ERROR_SRI_CORRUPT;
if (request->mIntegrity.IsEmpty() ||
NS_SUCCEEDED(SRICheck::VerifyIntegrity(request->mIntegrity, aLoader,
request->mCORSMode, aStringLen,
aString, mDocument))) {
rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen, aString);
nsresult rv = NS_OK;
if (!request->mIntegrity.IsEmpty() &&
NS_SUCCEEDED((rv = aSRIStatus))) {
MOZ_ASSERT(aSRIDataVerifier);
nsCOMPtr<nsIRequest> channelRequest;
aLoader->GetRequest(getter_AddRefs(channelRequest));
nsCOMPtr<nsIChannel> channel;
channel = do_QueryInterface(channelRequest);
if (NS_FAILED(aSRIDataVerifier->Verify(request->mIntegrity, channel,
request->mCORSMode, mDocument))) {
rv = NS_ERROR_SRI_CORRUPT;
}
}
if (NS_SUCCEEDED(rv)) {
rv = PrepareLoadedRequest(request, aLoader, aChannelStatus, aString);
}
if (NS_FAILED(rv)) {
@ -1497,16 +1483,12 @@ nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
} else {
mPreloads.RemoveElement(request, PreloadRequestComparator());
}
rv = NS_OK;
} else {
free(const_cast<uint8_t *>(aString));
rv = NS_SUCCESS_ADOPTED_DATA;
}
// Process our request and/or any pending ones
ProcessPendingRequests();
return rv;
return NS_OK;
}
void
@ -1535,10 +1517,9 @@ nsScriptLoader::NumberOfProcessors()
nsresult
nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
nsIStreamLoader* aLoader,
nsIIncrementalStreamLoader* aLoader,
nsresult aStatus,
uint32_t aStringLen,
const uint8_t* aString)
mozilla::Vector<char16_t> &aString)
{
if (NS_FAILED(aStatus)) {
return aStatus;
@ -1586,21 +1567,9 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
NS_ENSURE_SUCCESS(rv, rv);
}
if (aStringLen) {
// Check the charset attribute to determine script charset.
nsAutoString hintCharset;
if (!aRequest->IsPreload()) {
aRequest->mElement->GetScriptCharset(hintCharset);
} else {
nsTArray<PreloadInfo>::index_type i =
mPreloads.IndexOf(aRequest, 0, PreloadRequestComparator());
NS_ASSERTION(i != mPreloads.NoIndex, "Incorrect preload bookkeeping");
hintCharset = mPreloads[i].mCharset;
}
rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument,
aRequest->mScriptTextBuf, aRequest->mScriptTextLength);
NS_ENSURE_SUCCESS(rv, rv);
if (!aString.empty()) {
aRequest->mScriptTextLength = aString.length();
aRequest->mScriptTextBuf = aString.extractRawBuffer();
}
// This assertion could fire errorously if we ran out of memory when
@ -1739,3 +1708,192 @@ nsScriptLoader::MaybeRemovedDeferRequests()
}
return false;
}
//////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////
nsScriptLoadHandler::nsScriptLoadHandler(nsScriptLoader *aScriptLoader,
nsScriptLoadRequest *aRequest,
mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier)
: mScriptLoader(aScriptLoader),
mRequest(aRequest),
mSRIDataVerifier(aSRIDataVerifier),
mSRIStatus(NS_OK),
mDecoder(),
mBuffer()
{}
nsScriptLoadHandler::~nsScriptLoadHandler()
{}
NS_IMPL_ISUPPORTS(nsScriptLoadHandler, nsIIncrementalStreamLoaderObserver)
NS_IMETHODIMP
nsScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
uint32_t aDataLength,
const uint8_t* aData,
uint32_t *aConsumedLength)
{
if (mRequest->IsCanceled()) {
// If request cancelled, ignore any incoming data.
*aConsumedLength = aDataLength;
return NS_OK;
}
if (!EnsureDecoder(aLoader, aData, aDataLength,
/* aEndOfStream = */ false)) {
return NS_OK;
}
// Below we will/shall consume entire data chunk.
*aConsumedLength = aDataLength;
// Decoder has already been initialized. -- trying to decode all loaded bytes.
nsresult rv = TryDecodeRawData(aData, aDataLength,
/* aEndOfStream = */ false);
NS_ENSURE_SUCCESS(rv, rv);
// If SRI is required for this load, appending new bytes to the hash.
if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
}
return rv;
}
nsresult
nsScriptLoadHandler::TryDecodeRawData(const uint8_t* aData,
uint32_t aDataLength,
bool aEndOfStream)
{
int32_t srcLen = aDataLength;
const char* src = reinterpret_cast<const char *>(aData);
int32_t dstLen;
nsresult rv =
mDecoder->GetMaxLength(src, srcLen, &dstLen);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t haveRead = mBuffer.length();
uint32_t capacity = haveRead + dstLen;
if (!mBuffer.reserve(capacity)) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = mDecoder->Convert(src,
&srcLen,
mBuffer.begin() + haveRead,
&dstLen);
NS_ENSURE_SUCCESS(rv, rv);
haveRead += dstLen;
MOZ_ASSERT(haveRead <= capacity, "mDecoder produced more data than expected");
mBuffer.resizeUninitialized(haveRead);
return NS_OK;
}
bool
nsScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader *aLoader,
const uint8_t* aData,
uint32_t aDataLength,
bool aEndOfStream)
{
// Check if decoder has already been created.
if (mDecoder) {
return true;
}
nsAutoCString charset;
// Determine if BOM check should be done. This occurs either
// if end-of-stream has been reached, or at least 3 bytes have
// been read from input.
if (!aEndOfStream && (aDataLength < 3)) {
return false;
}
// Do BOM detection.
if (DetectByteOrderMark(aData, aDataLength, charset)) {
mDecoder = EncodingUtils::DecoderForEncoding(charset);
return true;
}
// BOM detection failed, check content stream for charset.
nsCOMPtr<nsIRequest> req;
nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
NS_ASSERTION(req, "StreamLoader's request went away prematurely");
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<nsIChannel> channel = do_QueryInterface(req);
if (channel &&
NS_SUCCEEDED(channel->GetContentCharset(charset)) &&
EncodingUtils::FindEncodingForLabel(charset, charset)) {
mDecoder = EncodingUtils::DecoderForEncoding(charset);
return true;
}
// Check the hint charset from the script element or preload
// request.
nsAutoString hintCharset;
if (!mRequest->IsPreload()) {
mRequest->mElement->GetScriptCharset(hintCharset);
} else {
nsTArray<nsScriptLoader::PreloadInfo>::index_type i =
mScriptLoader->mPreloads.IndexOf(mRequest, 0,
nsScriptLoader::PreloadRequestComparator());
NS_ASSERTION(i != mScriptLoader->mPreloads.NoIndex,
"Incorrect preload bookkeeping");
hintCharset = mScriptLoader->mPreloads[i].mCharset;
}
if (EncodingUtils::FindEncodingForLabel(hintCharset, charset)) {
mDecoder = EncodingUtils::DecoderForEncoding(charset);
return true;
}
// Get the charset from the charset of the document.
if (mScriptLoader->mDocument) {
charset = mScriptLoader->mDocument->GetDocumentCharacterSet();
mDecoder = EncodingUtils::DecoderForEncoding(charset);
return true;
}
// Curiously, there are various callers that don't pass aDocument. The
// fallback in the old code was ISO-8859-1, which behaved like
// windows-1252. Saying windows-1252 for clarity and for compliance
// with the Encoding Standard.
charset = "windows-1252";
mDecoder = EncodingUtils::DecoderForEncoding(charset);
return true;
}
NS_IMETHODIMP
nsScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
nsresult aStatus,
uint32_t aDataLength,
const uint8_t* aData)
{
if (!mRequest->IsCanceled()) {
DebugOnly<bool> encoderSet =
EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true);
MOZ_ASSERT(encoderSet);
DebugOnly<nsresult> rv = TryDecodeRawData(aData, aDataLength,
/* aEndOfStream = */ true);
// If SRI is required for this load, appending new bytes to the hash.
if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
}
}
// we have to mediate and use mRequest.
return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, mSRIStatus,
mBuffer, mSRIDataVerifier);
}

View File

@ -12,16 +12,19 @@
#define __nsScriptLoader_h__
#include "nsCOMPtr.h"
#include "nsIUnicodeDecoder.h"
#include "nsIScriptElement.h"
#include "nsCOMArray.h"
#include "nsTArray.h"
#include "nsAutoPtr.h"
#include "nsIDocument.h"
#include "nsIStreamLoader.h"
#include "nsIIncrementalStreamLoader.h"
#include "mozilla/CORSMode.h"
#include "mozilla/dom/SRIMetadata.h"
#include "mozilla/dom/SRICheck.h"
#include "mozilla/LinkedList.h"
#include "mozilla/net/ReferrerPolicy.h"
#include "mozilla/Vector.h"
class nsScriptLoadRequestList;
class nsIURI;
@ -195,7 +198,7 @@ public:
// Script loader implementation
//////////////////////////////////////////////////////////////
class nsScriptLoader final : public nsIStreamLoaderObserver
class nsScriptLoader final : public nsISupports
{
class MOZ_STACK_CLASS AutoCurrentScriptUpdater
{
@ -217,13 +220,13 @@ class nsScriptLoader final : public nsIStreamLoaderObserver
};
friend class nsScriptRequestProcessor;
friend class nsScriptLoadHandler;
friend class AutoCurrentScriptUpdater;
public:
explicit nsScriptLoader(nsIDocument* aDocument);
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLOADEROBSERVER
/**
* The loader maintains a weak reference to the document with
@ -342,6 +345,18 @@ public:
nsIDocument* aDocument,
char16_t*& aBufOut, size_t& aLengthOut);
/**
* Handle the completion of a stream. This is called by the
* nsScriptLoadHandler object which observes the IncrementalStreamLoader
* loading the script.
*/
nsresult OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
nsresult aChannelStatus,
nsresult aSRIStatus,
mozilla::Vector<char16_t> &aString,
mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier);
/**
* Processes any pending requests that are ready for processing.
*/
@ -489,10 +504,9 @@ private:
uint32_t NumberOfProcessors();
nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
nsIStreamLoader* aLoader,
nsIIncrementalStreamLoader* aLoader,
nsresult aStatus,
uint32_t aStringLen,
const uint8_t* aString);
mozilla::Vector<char16_t> &aString);
void AddDeferRequest(nsScriptLoadRequest* aRequest);
bool MaybeRemovedDeferRequests();
@ -538,6 +552,53 @@ private:
bool mBlockingDOMContentLoaded;
};
class nsScriptLoadHandler final : public nsIIncrementalStreamLoaderObserver
{
public:
explicit nsScriptLoadHandler(nsScriptLoader* aScriptLoader,
nsScriptLoadRequest *aRequest,
mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier);
NS_DECL_ISUPPORTS
NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
private:
virtual ~nsScriptLoadHandler();
/*
* Try to decode some raw data.
*/
nsresult TryDecodeRawData(const uint8_t* aData, uint32_t aDataLength,
bool aEndOfStream);
/*
* Discover the charset by looking at the stream data, the script
* tag, and other indicators. Returns true if charset has been
* discovered.
*/
bool EnsureDecoder(nsIIncrementalStreamLoader *aLoader,
const uint8_t* aData, uint32_t aDataLength,
bool aEndOfStream);
// ScriptLoader which will handle the parsed script.
RefPtr<nsScriptLoader> mScriptLoader;
// The nsScriptLoadRequest for this load.
RefPtr<nsScriptLoadRequest> mRequest;
// SRI data verifier.
nsAutoPtr<mozilla::dom::SRICheckDataVerifier> mSRIDataVerifier;
// Status of SRI data operations.
nsresult mSRIStatus;
// Unicode decoder for charset.
nsCOMPtr<nsIUnicodeDecoder> mDecoder;
// Accumulated decoded char buffer.
mozilla::Vector<char16_t> mBuffer;
};
class nsAutoScriptLoaderDisabler
{
public:

View File

@ -2534,6 +2534,55 @@ CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissio
return true;
}
bool
IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal,
uint32_t aNonExposedGlobals)
{
MOZ_ASSERT(aNonExposedGlobals, "Why did we get called?");
MOZ_ASSERT((aNonExposedGlobals &
~(GlobalNames::Window |
GlobalNames::BackstagePass |
GlobalNames::DedicatedWorkerGlobalScope |
GlobalNames::SharedWorkerGlobalScope |
GlobalNames::ServiceWorkerGlobalScope |
GlobalNames::WorkerDebuggerGlobalScope)) == 0,
"Unknown non-exposed global type");
const char* name = js::GetObjectClass(aGlobal)->name;
if ((aNonExposedGlobals & GlobalNames::Window) &&
!strcmp(name, "Window")) {
return true;
}
if ((aNonExposedGlobals & GlobalNames::BackstagePass) &&
!strcmp(name, "BackstagePass")) {
return true;
}
if ((aNonExposedGlobals & GlobalNames::DedicatedWorkerGlobalScope) &&
!strcmp(name, "DedicatedWorkerGlobalScope")) {
return true;
}
if ((aNonExposedGlobals & GlobalNames::SharedWorkerGlobalScope) &&
!strcmp(name, "SharedWorkerGlobalScope")) {
return true;
}
if ((aNonExposedGlobals & GlobalNames::ServiceWorkerGlobalScope) &&
!strcmp(name, "ServiceWorkerGlobalScope")) {
return true;
}
if ((aNonExposedGlobals & GlobalNames::WorkerDebuggerGlobalScope) &&
!strcmp(name, "WorkerDebuggerGlobalScopex")) {
return true;
}
return false;
}
void
HandlePrerenderingViolation(nsPIDOMWindow* aWindow)
{

View File

@ -3160,16 +3160,6 @@ AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
JS::Handle<JS::Value> aValue);
#endif
// Returns true if aObj's global has any of the permissions named in aPermissions
// set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be null-terminated.
bool
CheckAnyPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
// Returns true if aObj's global has all of the permissions named in aPermissions
// set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be null-terminated.
bool
CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
// This function is called by the bindings layer for methods/getters/setters
// that are not safe to be called in prerendering mode. It checks to make sure
// that the |this| object is not running in a global that is in prerendering

View File

@ -1906,16 +1906,26 @@ def getAvailableInTestFunc(obj):
class MemberCondition:
"""
An object representing the condition for a member to actually be
exposed. Any of pref, func, and available can be None. If not
None, they should be strings that have the pref name (for "pref")
or function name (for "func" and "available").
exposed. Any of the arguments can be None. If not
None, they should have the following types:
pref: The name of the preference.
func: The name of the function.
available: A string indicating where we should be available.
checkAnyPermissions: An integer index for the anypermissions_* to use.
checkAllPermissions: An integer index for the allpermissions_* to use.
nonExposedGlobals: A set of names of globals. Can be empty, in which case
it's treated the same way as None.
"""
def __init__(self, pref, func, available=None, checkAnyPermissions=None, checkAllPermissions=None):
def __init__(self, pref=None, func=None, available=None,
checkAnyPermissions=None, checkAllPermissions=None,
nonExposedGlobals=None):
assert pref is None or isinstance(pref, str)
assert func is None or isinstance(func, str)
assert available is None or isinstance(available, str)
assert checkAnyPermissions is None or isinstance(checkAnyPermissions, int)
assert checkAllPermissions is None or isinstance(checkAllPermissions, int)
assert nonExposedGlobals is None or isinstance(nonExposedGlobals, set)
self.pref = pref
def toFuncPtr(val):
@ -1933,11 +1943,20 @@ class MemberCondition:
else:
self.checkAllPermissions = "allpermissions_%i" % checkAllPermissions
if nonExposedGlobals:
# Nonempty set
self.nonExposedGlobals = " | ".join(
map(lambda g: "GlobalNames::%s" % g,
sorted(nonExposedGlobals)))
else:
self.nonExposedGlobals = "0"
def __eq__(self, other):
return (self.pref == other.pref and self.func == other.func and
self.available == other.available and
self.checkAnyPermissions == other.checkAnyPermissions and
self.checkAllPermissions == other.checkAllPermissions)
self.checkAllPermissions == other.checkAllPermissions and
self.nonExposedGlobals == other.nonExposedGlobals)
def __ne__(self, other):
return not self.__eq__(other)
@ -2000,13 +2019,34 @@ class PropertyDefiner:
@staticmethod
def getControllingCondition(interfaceMember, descriptor):
return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember,
"Pref"),
PropertyDefiner.getStringAttr(interfaceMember,
"Func"),
getAvailableInTestFunc(interfaceMember),
descriptor.checkAnyPermissionsIndicesForMembers.get(interfaceMember.identifier.name),
descriptor.checkAllPermissionsIndicesForMembers.get(interfaceMember.identifier.name))
# We do a slightly complicated thing for exposure sets to deal nicely
# with the situation of an [Exposed=Window] thing on an interface
# exposed in workers that has a worker-specific descriptor. In that
# situation, we already skip generation of the member entirely in the
# worker binding, and shouldn't need to check for the various worker
# scopes in the non-worker binding.
interface = descriptor.interface
nonExposureSet = interface.exposureSet - interfaceMember.exposureSet
# Skip getting the descriptor if we're just exposed everywhere or not
# looking at the non-worker descriptor.
if len(nonExposureSet) and not descriptor.workers:
workerProvider = descriptor.config.getDescriptorProvider(True)
workerDesc = workerProvider.getDescriptor(interface.identifier.name)
if workerDesc.workers:
# Just drop all the worker interface names from the
# nonExposureSet, since we know we'll have a mainthread global
# of some sort.
nonExposureSet.difference_update(interface.getWorkerExposureSet())
return MemberCondition(
PropertyDefiner.getStringAttr(interfaceMember,
"Pref"),
PropertyDefiner.getStringAttr(interfaceMember,
"Func"),
getAvailableInTestFunc(interfaceMember),
descriptor.checkAnyPermissionsIndicesForMembers.get(interfaceMember.identifier.name),
descriptor.checkAllPermissionsIndicesForMembers.get(interfaceMember.identifier.name),
nonExposureSet)
def generatePrefableArray(self, array, name, specFormatter, specTerminator,
specType, getCondition, getDataTuple, doIdArrays):
@ -2044,7 +2084,7 @@ class PropertyDefiner:
specs = []
prefableSpecs = []
prefableTemplate = ' { true, %s, %s, %s, %s, &%s[%d] }'
prefableTemplate = ' { true, %s, %s, %s, %s, %s, &%s[%d] }'
prefCacheTemplate = '&%s[%d].enabled'
def switchToCondition(props, condition):
@ -2056,7 +2096,8 @@ class PropertyDefiner:
prefCacheTemplate % (name, len(prefableSpecs))))
# Set up pointers to the new sets of specs inside prefableSpecs
prefableSpecs.append(prefableTemplate %
(condition.func,
(condition.nonExposedGlobals,
condition.func,
condition.available,
condition.checkAnyPermissions,
condition.checkAllPermissions,
@ -2075,7 +2116,7 @@ class PropertyDefiner:
# And the actual spec
specs.append(specFormatter(getDataTuple(member)))
specs.append(specTerminator)
prefableSpecs.append(" { false, nullptr }")
prefableSpecs.append(" { false, 0, nullptr, nullptr, nullptr, nullptr, nullptr }")
specType = "const " + specType
arrays = fill(
@ -2198,7 +2239,7 @@ class MethodDefiner(PropertyDefiner):
"methodInfo": False,
"length": 1,
"flags": "0",
"condition": MemberCondition(None, condition)
"condition": MemberCondition(func=condition)
})
continue
@ -2252,7 +2293,7 @@ class MethodDefiner(PropertyDefiner):
"selfHostedName": "ArrayValues",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": MemberCondition(None, None)
"condition": MemberCondition()
})
# Output an @@iterator for generated iterator interfaces. This should
@ -2268,7 +2309,7 @@ class MethodDefiner(PropertyDefiner):
"selfHostedName": "IteratorIdentity",
"length": 0,
"flags": "0",
"condition": MemberCondition(None, None)
"condition": MemberCondition()
})
# Generate the maplike/setlike iterator, if one wasn't already
@ -2341,7 +2382,7 @@ class MethodDefiner(PropertyDefiner):
"length": 0,
"flags": "JSPROP_ENUMERATE", # readonly/permanent added
# automatically.
"condition": MemberCondition(None, None)
"condition": MemberCondition()
})
if descriptor.interface.isJSImplemented():
@ -2353,7 +2394,7 @@ class MethodDefiner(PropertyDefiner):
"methodInfo": False,
"length": 2,
"flags": "0",
"condition": MemberCondition(None, None)
"condition": MemberCondition()
})
else:
for m in clearableCachedAttrs(descriptor):
@ -2364,7 +2405,7 @@ class MethodDefiner(PropertyDefiner):
"methodInfo": False,
"length": "0",
"flags": "0",
"condition": MemberCondition(None, None)
"condition": MemberCondition()
})
self.unforgeable = unforgeable

View File

@ -39,12 +39,24 @@ typedef bool
JS::Handle<JSObject*> obj,
JS::AutoIdVector& props);
// Returns true if aObj's global has any of the permissions named in
// aPermissions set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be
// null-terminated.
bool
CheckAnyPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
// Returns true if aObj's global has all of the permissions named in
// aPermissions set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be
// null-terminated.
bool
CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
// Returns true if the given global is of a type whose bit is set in
// aNonExposedGlobals.
bool
IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal,
uint32_t aNonExposedGlobals);
struct ConstantSpec
{
const char* name;
@ -53,9 +65,35 @@ struct ConstantSpec
typedef bool (*PropertyEnabled)(JSContext* cx, JSObject* global);
namespace GlobalNames {
// The names of our possible globals. These are the names of the actual
// interfaces, not of the global names used to refer to them in IDL [Exposed]
// annotations.
static const uint32_t Window = 1u << 0;
static const uint32_t BackstagePass = 1u << 1;
static const uint32_t DedicatedWorkerGlobalScope = 1u << 2;
static const uint32_t SharedWorkerGlobalScope = 1u << 3;
static const uint32_t ServiceWorkerGlobalScope = 1u << 4;
static const uint32_t WorkerDebuggerGlobalScope = 1u << 5;
} // namespace GlobalNames
template<typename T>
struct Prefable {
inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
// Reading "enabled" on a worker thread is technically undefined behavior,
// because it's written only on main threads, with no barriers of any sort.
// So we want to avoid doing that. But we don't particularly want to make
// expensive NS_IsMainThread calls here.
//
// The good news is that "enabled" is only written for things that have a
// Pref annotation, and such things can never be exposed on non-Window
// globals; our IDL parser enforces that. So as long as we check our
// exposure set before checking "enabled" we will be ok.
if (nonExposedGlobals &&
IsNonExposedGlobal(cx, js::GetGlobalForObjectCrossCompartment(obj),
nonExposedGlobals)) {
return false;
}
if (!enabled) {
return false;
}
@ -85,6 +123,8 @@ struct Prefable {
// A boolean indicating whether this set of specs is enabled
bool enabled;
// Bitmask of global names that we should not be exposed in.
uint32_t nonExposedGlobals;
// A function pointer to a function that can say the property is disabled
// even if "enabled" is set to true. If the pointer is null the value of
// "enabled" is used as-is unless availableFunc overrides.

View File

@ -46,7 +46,9 @@ WebGLBuffer::BindTo(GLenum target)
case LOCAL_GL_COPY_READ_BUFFER:
case LOCAL_GL_COPY_WRITE_BUFFER:
/* Do nothing. Doesn't set the type of the buffer contents. */
if (mContent == Kind::Undefined) {
mContent = Kind::OtherData;
}
break;
default:

View File

@ -12,9 +12,7 @@ namespace mozilla {
WebGLVertexArrayGL::WebGLVertexArrayGL(WebGLContext* webgl)
: WebGLVertexArray(webgl)
#if defined(XP_LINUX)
, mIsVAO(false)
#endif
{ }
WebGLVertexArrayGL::~WebGLVertexArrayGL()
@ -30,9 +28,7 @@ WebGLVertexArrayGL::DeleteImpl()
mContext->MakeContextCurrent();
mContext->gl->fDeleteVertexArrays(1, &mGLName);
#if defined(XP_LINUX)
mIsVAO = false;
#endif
}
void
@ -41,9 +37,7 @@ WebGLVertexArrayGL::BindVertexArrayImpl()
mContext->mBoundVertexArray = this;
mContext->gl->fBindVertexArray(mGLName);
#if defined(XP_LINUX)
mIsVAO = true;
#endif
}
void
@ -55,15 +49,11 @@ WebGLVertexArrayGL::GenVertexArray()
bool
WebGLVertexArrayGL::IsVertexArrayImpl()
{
#if defined(XP_LINUX)
gl::GLContext* gl = mContext->gl;
if (gl->WorkAroundDriverBugs() &&
gl->Vendor() == gl::GLVendor::VMware &&
gl->Renderer() == gl::GLRenderer::GalliumLlvmpipe)
if (gl->WorkAroundDriverBugs())
{
return mIsVAO;
}
#endif
mContext->MakeContextCurrent();
return mContext->gl->fIsVertexArray(mGLName) != 0;

View File

@ -25,13 +25,11 @@ protected:
explicit WebGLVertexArrayGL(WebGLContext* webgl);
~WebGLVertexArrayGL();
#if defined(XP_LINUX)
// Bug 1140459: Some drivers (including our test slaves!) don't
// give reasonable answers for IsRenderbuffer, maybe others.
// give reasonable answers for IsVertexArray, maybe others.
//
// So we track the `is a VAO` state ourselves.
bool mIsVAO;
#endif
};
} // namespace mozilla

View File

@ -28,6 +28,7 @@ CaptureStreamTestHelper.prototype = {
blackTransparent: { data: [0, 0, 0, 0], name: "blackTransparent" },
green: { data: [0, 255, 0, 255], name: "green" },
red: { data: [255, 0, 0, 255], name: "red" },
grey: { data: [128, 128, 128, 255], name: "grey" },
/* Default element size for createAndAppendElement() */
elemWidth: 100,
@ -97,7 +98,12 @@ CaptureStreamTestHelper.prototype = {
const startTime = video.currentTime;
CaptureStreamTestHelper2D.prototype.clear.call(this, this.cout);
var ontimeupdate = () => {
const pixelMatch = test(this.getPixel(video, offsetX, offsetY));
var pixelMatch = false;
try {
pixelMatch = test(this.getPixel(video, offsetX, offsetY));
} catch (NS_ERROR_NOT_AVAILABLE) {
info("Waiting for pixel but no video available");
}
if (!pixelMatch &&
(!timeout || video.currentTime < startTime + (timeout / 1000.0))) {
// No match yet and,

View File

@ -8,6 +8,7 @@
#include "mozilla/IMEStateManager.h"
#include "mozilla/TextEvents.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLUnknownElement.h"
#include "mozilla/dom/Selection.h"
#include "nsCaret.h"
#include "nsCOMPtr.h"
@ -24,6 +25,7 @@
#include "nsIObjectFrame.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h"
#include "nsQueryObject.h"
#include "nsRange.h"
#include "nsTextFragment.h"
#include "nsTextFrame.h"
@ -40,6 +42,65 @@ using namespace widget;
/* ContentEventHandler */
/******************************************************************/
// NOTE
//
// ContentEventHandler *creates* ranges as following rules:
// 1. Start of range:
// 1.1. Cases: [textNode or text[Node or textNode[
// When text node is start of a range, start node is the text node and
// start offset is any number between 0 and the length of the text.
// 1.2. Case: [<element>:
// When start of an element node is start of a range, start node is
// parent of the element and start offset is the element's index in the
// parent.
// 1.3. Case: <element/>[
// When after an empty element node is start of a range, start node is
// parent of the element and start offset is the element's index in the
// parent + 1.
// 1.4. Case: <element>[
// When start of a non-empty element is start of a range, start node is
// the element and start offset is 0.
// 1.5. Case: <root>[
// When start of a range is 0 and there are no nodes causing text,
// start node is the root node and start offset is 0.
// 1.6. Case: [</root>
// When start of a range is out of bounds, start node is the root node
// and start offset is number of the children.
// 2. End of range:
// 2.1. Cases: ]textNode or text]Node or textNode]
// When a text node is end of a range, end node is the text node and
// end offset is any number between 0 and the length of the text.
// 2.2. Case: ]<element>
// When before an element node (meaning before the open tag of the
// element) is end of a range, end node is previous node causing text.
// Note that this case shouldn't be handled directly. If rule 2.1 and
// 2.3 are handled correctly, the loop with nsContentIterator shouldn't
// reach the element node since the loop should've finished already at
// handling the last node which caused some text.
// 2.3. Case: <element>]
// When a line break is caused before a non-empty element node and it's
// end of a range, end node is the element and end offset is 0.
// (i.e., including open tag of the element)
// 2.4. Cases: <element/>]
// When after an empty element node is end of a range, end node is
// parent of the element node and end offset is the element's index in
// the parent + 1. (i.e., including close tag of the element or empty
// element)
// 2.5. Case: ]</root>
// When end of a range is out of bounds, end node is the root node and
// end offset is number of the children.
//
// ContentEventHandler *treats* ranges as following additional rules:
// 1. When the start node is an element node which doesn't have children,
// it includes a line break caused before itself (i.e., includes its open
// tag). For example, if start position is { <br>, 0 }, the line break
// caused by <br> should be included into the flatten text.
// 2. When the end node is an element node which doesn't have children,
// it includes the end (i.e., includes its close tag except empty element).
// Although, currently, any close tags don't cause line break, this also
// includes its open tag. For example, if end position is { <br>, 0 }, the
// line break caused by the <br> should be included into the flatten text.
ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext)
: mPresContext(aPresContext)
, mPresShell(aPresContext->GetPresShell())
@ -327,6 +388,9 @@ ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
{
MOZ_ASSERT(aEndOffset >= aStartOffset,
"aEndOffset must be equals or larger than aStartOffset");
if (NS_WARN_IF(!aContent->IsNodeOfType(nsINode::eTEXT))) {
return 0;
}
if (aStartOffset == aEndOffset) {
return 0;
}
@ -338,11 +402,25 @@ ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
uint32_t aMaxLength)
{
if (NS_WARN_IF(!aContent->IsNodeOfType(nsINode::eTEXT))) {
return 0;
}
return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength);
}
static inline uint32_t
GetBRLength(LineBreakType aLineBreakType)
/* static */ uint32_t
ContentEventHandler::GetNativeTextLengthBefore(nsIContent* aContent,
nsINode* aRootNode)
{
if (NS_WARN_IF(aContent->IsNodeOfType(nsINode::eTEXT))) {
return 0;
}
return ShouldBreakLineBefore(aContent, aRootNode) ?
GetBRLength(LINE_BREAK_TYPE_NATIVE) : 0;
}
/* static inline */ uint32_t
ContentEventHandler::GetBRLength(LineBreakType aLineBreakType)
{
#if defined(XP_WIN)
// Length of \r\n
@ -357,29 +435,26 @@ ContentEventHandler::GetTextLength(nsIContent* aContent,
LineBreakType aLineBreakType,
uint32_t aMaxLength)
{
if (aContent->IsNodeOfType(nsINode::eTEXT)) {
uint32_t textLengthDifference =
MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
uint32_t textLengthDifference =
#if defined(XP_WIN)
// On Windows, the length of a native newline ("\r\n") is twice the length
// of the XP newline ("\n"), so XP length is equal to the length of the
// native offset plus the number of newlines encountered in the string.
(aLineBreakType == LINE_BREAK_TYPE_NATIVE) ?
CountNewlinesInXPLength(aContent, aMaxLength) : 0;
// On Windows, the length of a native newline ("\r\n") is twice the length
// of the XP newline ("\n"), so XP length is equal to the length of the
// native offset plus the number of newlines encountered in the string.
(aLineBreakType == LINE_BREAK_TYPE_NATIVE) ?
CountNewlinesInXPLength(aContent, aMaxLength) : 0;
#else
// On other platforms, the native and XP newlines are the same.
0;
// On other platforms, the native and XP newlines are the same.
0;
#endif
const nsTextFragment* text = aContent->GetText();
if (!text) {
return 0;
}
uint32_t length = std::min(text->GetLength(), aMaxLength);
return length + textLengthDifference;
} else if (IsContentBR(aContent)) {
return GetBRLength(aLineBreakType);
const nsTextFragment* text = aContent->GetText();
if (!text) {
return 0;
}
return 0;
uint32_t length = std::min(text->GetLength(), aMaxLength);
return length + textLengthDifference;
}
static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
@ -395,37 +470,112 @@ static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
#endif
}
static nsresult GenerateFlatTextContent(nsRange* aRange,
nsAFlatString& aString,
LineBreakType aLineBreakType)
/* static */ bool
ContentEventHandler::ShouldBreakLineBefore(nsIContent* aContent,
nsINode* aRootNode)
{
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
iter->Init(aRange);
// We don't need to append linebreak at the start of the root element.
if (aContent == aRootNode) {
return false;
}
// If it's not an HTML element (including other markup language's elements),
// we shouldn't insert like break before that for now. Becoming this is a
// problem must be edge case. E.g., when ContentEventHandler is used with
// MathML or SVG elements.
if (!aContent->IsHTMLElement()) {
return false;
}
// If the element is <br>, we need to check if the <br> is caused by web
// content. Otherwise, i.e., it's caused by internal reason of Gecko,
// it shouldn't be exposed as a line break to flatten text.
if (aContent->IsHTMLElement(nsGkAtoms::br)) {
return IsContentBR(aContent);
}
// Note that ideally, we should refer the style of the primary frame of
// aContent for deciding if it's an inline. However, it's difficult
// IMEContentObserver to notify IME of text change caused by style change.
// Therefore, currently, we should check only from the tag for now.
if (aContent->IsAnyOfHTMLElements(nsGkAtoms::a,
nsGkAtoms::abbr,
nsGkAtoms::acronym,
nsGkAtoms::b,
nsGkAtoms::bdi,
nsGkAtoms::bdo,
nsGkAtoms::big,
nsGkAtoms::cite,
nsGkAtoms::code,
nsGkAtoms::data,
nsGkAtoms::del,
nsGkAtoms::dfn,
nsGkAtoms::em,
nsGkAtoms::font,
nsGkAtoms::i,
nsGkAtoms::ins,
nsGkAtoms::kbd,
nsGkAtoms::mark,
nsGkAtoms::s,
nsGkAtoms::samp,
nsGkAtoms::small,
nsGkAtoms::span,
nsGkAtoms::strike,
nsGkAtoms::strong,
nsGkAtoms::sub,
nsGkAtoms::sup,
nsGkAtoms::time,
nsGkAtoms::tt,
nsGkAtoms::u,
nsGkAtoms::var)) {
return false;
}
// If the element is unknown element, we shouldn't insert line breaks before
// it since unknown elements should be ignored.
RefPtr<HTMLUnknownElement> unknownHTMLElement = do_QueryObject(aContent);
return !unknownHTMLElement;
}
nsresult
ContentEventHandler::GenerateFlatTextContent(nsRange* aRange,
nsAFlatString& aString,
LineBreakType aLineBreakType)
{
NS_ASSERTION(aString.IsEmpty(), "aString must be empty string");
if (aRange->Collapsed()) {
return NS_OK;
}
nsINode* startNode = aRange->GetStartParent();
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
nsINode* endNode = aRange->GetEndParent();
NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
return NS_ERROR_FAILURE;
}
if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
nsIContent* content = static_cast<nsIContent*>(startNode);
nsIContent* content = startNode->AsContent();
AppendSubString(aString, content, aRange->StartOffset(),
aRange->EndOffset() - aRange->StartOffset());
ConvertToNativeNewlines(aString);
return NS_OK;
}
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
nsresult rv = iter->Init(aRange);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode();
if (!node) {
if (NS_WARN_IF(!node)) {
break;
}
if (!node->IsNodeOfType(nsINode::eCONTENT)) {
if (!node->IsContent()) {
continue;
}
nsIContent* content = static_cast<nsIContent*>(node);
nsIContent* content = node->AsContent();
if (content->IsNodeOfType(nsINode::eTEXT)) {
if (content == startNode) {
@ -436,7 +586,7 @@ static nsresult GenerateFlatTextContent(nsRange* aRange,
} else {
AppendString(aString, content);
}
} else if (IsContentBR(content)) {
} else if (ShouldBreakLineBefore(content, mRootContent)) {
aString.Append(char16_t('\n'));
}
}
@ -460,6 +610,8 @@ ContentEventHandler::GetTextLengthInRange(nsIContent* aContent,
uint32_t aXPEndOffset,
LineBreakType aLineBreakType)
{
MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
return aLineBreakType == LINE_BREAK_TYPE_NATIVE ?
GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) :
aXPEndOffset - aXPStartOffset;
@ -473,6 +625,8 @@ ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
int32_t aXPEndOffset,
LineBreakType aLineBreakType)
{
MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
nsIFrame* frame = aContent->GetPrimaryFrame();
if (!frame) {
// It is a non-rendered content, create an empty range for it.
@ -552,7 +706,7 @@ ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
}
}
/* static */ nsresult
nsresult
ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
FontRangeArray& aFontRanges,
uint32_t& aLength,
@ -560,16 +714,24 @@ ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
{
MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array");
if (aRange->Collapsed()) {
return NS_OK;
}
nsINode* startNode = aRange->GetStartParent();
nsINode* endNode = aRange->GetEndParent();
if (NS_WARN_IF(!startNode || !endNode)) {
if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
return NS_ERROR_FAILURE;
}
// baseOffset is the flattened offset of each content node.
int32_t baseOffset = 0;
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
for (iter->Init(aRange); !iter->IsDone(); iter->Next()) {
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
nsresult rv = iter->Init(aRange);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode();
if (NS_WARN_IF(!node)) {
break;
@ -587,7 +749,7 @@ ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
startOffset, endOffset, aLineBreakType);
baseOffset += GetTextLengthInRange(content, startOffset, endOffset,
aLineBreakType);
} else if (IsContentBR(content)) {
} else if (ShouldBreakLineBefore(content, mRootContent)) {
if (aFontRanges.IsEmpty()) {
MOZ_ASSERT(baseOffset == 0);
FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
@ -672,105 +834,221 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
*aNewOffset = aOffset;
}
// Special case like <br contenteditable>
if (!mRootContent->HasChildren()) {
nsresult rv = aRange->SetStart(mRootContent, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = aRange->SetEnd(mRootContent, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
nsresult rv = iter->Init(mRootContent);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
uint32_t offset = 0;
uint32_t endOffset = aOffset + aLength;
bool startSet = false;
for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode();
if (!node) {
if (NS_WARN_IF(!node)) {
break;
}
if (!node->IsNodeOfType(nsINode::eCONTENT)) {
// FYI: mRootContent shouldn't cause any text. So, we can skip it simply.
if (node == mRootContent || !node->IsContent()) {
continue;
}
nsIContent* content = static_cast<nsIContent*>(node);
nsIContent* content = node->AsContent();
uint32_t textLength = GetTextLength(content, aLineBreakType);
uint32_t textLength =
content->IsNodeOfType(nsINode::eTEXT) ?
GetTextLength(content, aLineBreakType) :
(ShouldBreakLineBefore(content, mRootContent) ?
GetBRLength(aLineBreakType) : 0);
if (!textLength) {
continue;
}
if (offset <= aOffset && aOffset < offset + textLength) {
uint32_t xpOffset;
if (!content->IsNodeOfType(nsINode::eTEXT)) {
xpOffset = 0;
} else {
xpOffset = aOffset - offset;
// When the start offset is in between accumulated offset and the last
// offset of the node, the node is the start node of the range.
if (!startSet && aOffset <= offset + textLength) {
nsINode* startNode = nullptr;
int32_t startNodeOffset = -1;
if (content->IsNodeOfType(nsINode::eTEXT)) {
// Rule #1.1: [textNode or text[Node or textNode[
uint32_t xpOffset = aOffset - offset;
if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
xpOffset = ConvertToXPOffset(content, xpOffset);
}
}
if (aExpandToClusterBoundaries) {
uint32_t oldXPOffset = xpOffset;
rv = ExpandToClusterBoundary(content, false, &xpOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (aNewOffset) {
// This is correct since a cluster shouldn't include line break.
*aNewOffset -= (oldXPOffset - xpOffset);
if (aExpandToClusterBoundaries) {
uint32_t oldXPOffset = xpOffset;
rv = ExpandToClusterBoundary(content, false, &xpOffset);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (aNewOffset) {
// This is correct since a cluster shouldn't include line break.
*aNewOffset -= (oldXPOffset - xpOffset);
}
}
startNode = content;
startNodeOffset = static_cast<int32_t>(xpOffset);
} else if (aOffset < offset + textLength) {
// Rule #1.2 [<element>
startNode = content->GetParent();
if (NS_WARN_IF(!startNode)) {
return NS_ERROR_FAILURE;
}
startNodeOffset = startNode->IndexOf(content);
if (NS_WARN_IF(startNodeOffset == -1)) {
// The content is being removed from the parent!
return NS_ERROR_FAILURE;
}
} else if (!content->HasChildren()) {
// Rule #1.3: <element/>[
startNode = content->GetParent();
if (NS_WARN_IF(!startNode)) {
return NS_ERROR_FAILURE;
}
startNodeOffset = startNode->IndexOf(content) + 1;
if (NS_WARN_IF(startNodeOffset == 0)) {
// The content is being removed from the parent!
return NS_ERROR_FAILURE;
}
} else {
// Rule #1.4: <element>[
startNode = content;
startNodeOffset = 0;
}
NS_ASSERTION(startNode, "startNode must not be nullptr");
NS_ASSERTION(startNodeOffset >= 0,
"startNodeOffset must not be negative");
rv = aRange->SetStart(startNode, startNodeOffset);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = aRange->SetStart(content, int32_t(xpOffset));
NS_ENSURE_SUCCESS(rv, rv);
startSet = true;
if (aLength == 0) {
// Ensure that the end offset and the start offset are same.
rv = aRange->SetEnd(content, int32_t(xpOffset));
NS_ENSURE_SUCCESS(rv, rv);
if (!aLength) {
rv = aRange->SetEnd(startNode, startNodeOffset);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
}
// When the end offset is in the content, the node is the end node of the
// range.
if (endOffset <= offset + textLength) {
nsINode* endNode = content;
uint32_t xpOffset;
MOZ_ASSERT(startSet,
"The start of the range should've been set already");
if (content->IsNodeOfType(nsINode::eTEXT)) {
xpOffset = endOffset - offset;
// Rule #2.1: ]textNode or text]Node or textNode]
uint32_t xpOffset = endOffset - offset;
if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
xpOffset = ConvertToXPOffset(content, xpOffset);
}
if (aExpandToClusterBoundaries) {
rv = ExpandToClusterBoundary(content, true, &xpOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
} else {
// Use first position of next node, because the end node is ignored
// by ContentIterator when the offset is zero.
xpOffset = 0;
iter->Next();
if (iter->IsDone()) {
break;
NS_ASSERTION(xpOffset <= INT32_MAX,
"The end node offset is too large");
rv = aRange->SetEnd(content, static_cast<int32_t>(xpOffset));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
endNode = iter->GetCurrentNode();
return NS_OK;
}
rv = aRange->SetEnd(endNode, int32_t(xpOffset));
NS_ENSURE_SUCCESS(rv, rv);
if (endOffset == offset) {
// Rule #2.2: ]<element>
// NOTE: Please don't crash on release builds because it must be
// overreaction but we shouldn't allow this bug when some
// automated tests find this.
MOZ_ASSERT(false, "This case should've already been handled at "
"the last node which caused some text");
return NS_ERROR_FAILURE;
}
if (content->HasChildren() &&
ShouldBreakLineBefore(content, mRootContent)) {
// Rule #2.3: </element>]
rv = aRange->SetEnd(content, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// Rule #2.4: <element/>]
nsINode* endNode = content->GetParent();
if (NS_WARN_IF(!endNode)) {
return NS_ERROR_FAILURE;
}
int32_t indexInParent = endNode->IndexOf(content);
if (NS_WARN_IF(indexInParent == -1)) {
// The content is being removed from the parent!
return NS_ERROR_FAILURE;
}
rv = aRange->SetEnd(endNode, indexInParent + 1);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
offset += textLength;
}
if (offset < aOffset) {
return NS_ERROR_FAILURE;
}
if (!startSet) {
MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT));
rv = aRange->SetStart(mRootContent, int32_t(mRootContent->GetChildCount()));
NS_ENSURE_SUCCESS(rv, rv);
if (!offset) {
// Rule #1.5: <root>[</root>
// When there are no nodes causing text, the start of the DOM range
// should be start of the root node since clicking on such editor (e.g.,
// <div contenteditable><span></span></div>) sets caret to the start of
// the editor (i.e., before <span> in the example).
rv = aRange->SetStart(mRootContent, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!aLength) {
rv = aRange->SetEnd(mRootContent, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
} else {
// Rule #1.5: [</root>
rv = aRange->SetStart(mRootContent,
static_cast<int32_t>(mRootContent->GetChildCount()));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
if (aNewOffset) {
*aNewOffset = offset;
}
}
rv = aRange->SetEnd(mRootContent, int32_t(mRootContent->GetChildCount()));
NS_ASSERTION(NS_SUCCEEDED(rv), "nsRange::SetEnd failed");
return rv;
// Rule #2.5: ]</root>
rv = aRange->SetEnd(mRootContent,
static_cast<int32_t>(mRootContent->GetChildCount()));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
/* static */ LineBreakType
@ -849,8 +1127,8 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
"The reply string must be empty");
LineBreakType lineBreakType = GetLineBreakType(aEvent);
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange,
&aEvent->mReply.mOffset, lineBreakType);
rv = GetFlatTextLengthBefore(mFirstSelectedRange,
&aEvent->mReply.mOffset, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINode> anchorNode, focusNode;
@ -1121,8 +1399,8 @@ ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect);
if (caretFrame) {
uint32_t offset;
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset,
lineBreakType);
rv = GetFlatTextLengthBefore(mFirstSelectedRange,
&offset, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
if (offset == aEvent->mInput.mOffset) {
rv = ConvertToRootRelativeOffset(caretFrame, caretRect);
@ -1301,8 +1579,9 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent)
return NS_OK;
}
rv = GetFlatTextOffsetOfRange(mRootContent, tentativeCaretOffsets.content,
tentativeCaretOffsets.offset,
rv = GetFlatTextLengthInRange(NodePosition(mRootContent, 0),
NodePosition(tentativeCaretOffsets),
mRootContent,
&aEvent->mReply.mTentativeCaretOffset,
GetLineBreakType(aEvent));
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -1325,10 +1604,13 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent)
textframe->GetCharacterOffsetAtFramePoint(ptInTarget);
NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE);
uint32_t offset;
rv = GetFlatTextOffsetOfRange(mRootContent, contentOffsets.content,
contentOffsets.offset, &offset,
rv = GetFlatTextLengthInRange(NodePosition(mRootContent, 0),
NodePosition(contentOffsets),
mRootContent, &offset,
GetLineBreakType(aEvent));
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
WidgetQueryContentEvent textRect(true, eQueryTextRect, aEvent->widget);
textRect.InitForQueryTextRect(offset, 1, aEvent->mUseNativeLineBreak);
@ -1390,82 +1672,158 @@ ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent)
}
/* static */ nsresult
ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsINode* aNode,
int32_t aNodeOffset,
uint32_t* aOffset,
LineBreakType aLineBreakType)
ContentEventHandler::GetFlatTextLengthInRange(
const NodePosition& aStartPosition,
const NodePosition& aEndPosition,
nsIContent* aRootContent,
uint32_t* aLength,
LineBreakType aLineBreakType,
bool aIsRemovingNode /* = false */)
{
NS_ENSURE_STATE(aRootContent);
NS_ASSERTION(aOffset, "param is invalid");
RefPtr<nsRange> prev = new nsRange(aRootContent);
nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(aRootContent));
prev->SetStart(rootDOMNode, 0);
nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(aNode));
NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode");
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
if (aNode->Length() >= static_cast<uint32_t>(aNodeOffset)) {
// Offset is within node's length; set end of range to that offset
prev->SetEnd(startDOMNode, aNodeOffset);
iter->Init(prev);
} else if (aNode != static_cast<nsINode*>(aRootContent)) {
// Offset is past node's length; set end of range to end of node
prev->SetEndAfter(startDOMNode);
iter->Init(prev);
} else {
// Offset is past the root node; set end of range to end of root node
iter->Init(aRootContent);
if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsValid()) ||
NS_WARN_IF(!aEndPosition.IsValid()) || NS_WARN_IF(!aLength)) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsINode> startNode = do_QueryInterface(startDOMNode);
nsINode* endNode = aNode;
if (aStartPosition == aEndPosition) {
*aLength = 0;
return NS_OK;
}
*aOffset = 0;
// Don't create nsContentIterator instance until it's really necessary since
// destroying without initializing causes unexpected NS_ASSERTION() call.
nsCOMPtr<nsIContentIterator> iter;
// This may be called for retrieving the text of removed nodes. Even in this
// case, the node thinks it's still in the tree because UnbindFromTree() will
// be called after here. However, the node was already removed from the
// array of children of its parent. So, be careful to handle this case.
if (aIsRemovingNode) {
DebugOnly<nsIContent*> parent = aStartPosition.mNode->GetParent();
MOZ_ASSERT(parent && parent->IndexOf(aStartPosition.mNode) == -1,
"At removing the node, the node shouldn't be in the array of children "
"of its parent");
MOZ_ASSERT(aStartPosition.mNode == aEndPosition.mNode,
"At removing the node, start and end node should be same");
MOZ_ASSERT(aStartPosition.mOffset == 0,
"When the node is being removed, the start offset should be 0");
MOZ_ASSERT(static_cast<uint32_t>(aEndPosition.mOffset) ==
aEndPosition.mNode->GetChildCount(),
"When the node is being removed, the end offset should be child count");
iter = NS_NewPreContentIterator();
nsresult rv = iter->Init(aStartPosition.mNode);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
RefPtr<nsRange> prev = new nsRange(aRootContent);
nsresult rv = aStartPosition.SetToRangeStart(prev);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// When the end position is immediately after non-root element's open tag,
// we need to include a line break caused by the open tag.
NodePosition endPosition;
if (aEndPosition.mNode != aRootContent &&
aEndPosition.IsImmediatelyAfterOpenTag()) {
if (aEndPosition.mNode->HasChildren()) {
// When the end node has some children, move the end position to the
// start of its first child.
nsINode* firstChild = aEndPosition.mNode->GetFirstChild();
if (NS_WARN_IF(!firstChild)) {
return NS_ERROR_FAILURE;
}
endPosition = NodePosition(firstChild, 0);
} else {
// When the end node is empty, move the end position after the node.
nsIContent* parentContent = aEndPosition.mNode->GetParent();
if (NS_WARN_IF(!parentContent)) {
return NS_ERROR_FAILURE;
}
int32_t indexInParent = parentContent->IndexOf(aEndPosition.mNode);
if (NS_WARN_IF(indexInParent < 0)) {
return NS_ERROR_FAILURE;
}
endPosition = NodePosition(parentContent, indexInParent + 1);
}
} else {
endPosition = aEndPosition;
}
if (endPosition.OffsetIsValid()) {
// Offset is within node's length; set end of range to that offset
rv = endPosition.SetToRangeEnd(prev);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
iter = NS_NewPreContentIterator();
rv = iter->Init(prev);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else if (endPosition.mNode != aRootContent) {
// Offset is past node's length; set end of range to end of node
rv = endPosition.SetToRangeEndAfter(prev);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
iter = NS_NewPreContentIterator();
rv = iter->Init(prev);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
// Offset is past the root node; set end of range to end of root node
iter = NS_NewPreContentIterator();
rv = iter->Init(aRootContent);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
}
*aLength = 0;
for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode();
if (!node) {
if (NS_WARN_IF(!node)) {
break;
}
if (!node->IsNodeOfType(nsINode::eCONTENT)) {
if (!node->IsContent()) {
continue;
}
nsIContent* content = static_cast<nsIContent*>(node);
nsIContent* content = node->AsContent();
if (node->IsNodeOfType(nsINode::eTEXT)) {
// Note: our range always starts from offset 0
if (node == endNode) {
*aOffset += GetTextLength(content, aLineBreakType, aNodeOffset);
if (node == aEndPosition.mNode) {
*aLength += GetTextLength(content, aLineBreakType,
aEndPosition.mOffset);
} else {
*aOffset += GetTextLength(content, aLineBreakType);
*aLength += GetTextLength(content, aLineBreakType);
}
} else if (IsContentBR(content)) {
#if defined(XP_WIN)
// On Windows, the length of the newline is 2.
*aOffset += (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
#else
// On other platforms, the length of the newline is 1.
*aOffset += 1;
#endif
} else if (ShouldBreakLineBefore(content, aRootContent)) {
// If the start position is start of this node but doesn't include the
// open tag, don't append the line break length.
if (node == aStartPosition.mNode && !aStartPosition.IsBeforeOpenTag()) {
continue;
}
*aLength += GetBRLength(aLineBreakType);
}
}
return NS_OK;
}
/* static */ nsresult
ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsRange* aRange,
uint32_t* aOffset,
LineBreakType aLineBreakType)
nsresult
ContentEventHandler::GetFlatTextLengthBefore(nsRange* aRange,
uint32_t* aOffset,
LineBreakType aLineBreakType)
{
nsINode* startNode = aRange->GetStartParent();
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
int32_t startOffset = aRange->StartOffset();
return GetFlatTextOffsetOfRange(aRootContent, startNode, startOffset,
aOffset, aLineBreakType);
MOZ_ASSERT(aRange);
return GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
NodePositionBefore(aRange->GetStartParent(), aRange->StartOffset()),
mRootContent, aOffset, aLineBreakType);
}
nsresult
@ -1571,33 +1929,36 @@ static void AdjustRangeForSelection(nsIContent* aRoot,
{
nsINode* node = *aNode;
int32_t nodeOffset = *aNodeOffset;
if (aRoot != node && node->GetParent()) {
if (node->IsNodeOfType(nsINode::eTEXT)) {
// When the offset is at the end of the text node, set it to after the
// text node, to make sure the caret is drawn on a new line when the last
// character of the text node is '\n'
int32_t nodeLength =
static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength());
MOZ_ASSERT(nodeOffset <= nodeLength, "Offset is past length of text node");
if (nodeOffset == nodeLength) {
node = node->GetParent();
nodeOffset = node->IndexOf(*aNode) + 1;
}
} else {
node = node->GetParent();
nodeOffset = node->IndexOf(*aNode) + (nodeOffset ? 1 : 0);
}
if (aRoot == node || NS_WARN_IF(!node->GetParent()) ||
!node->IsNodeOfType(nsINode::eTEXT)) {
return;
}
nsIContent* brContent = node->GetChildAt(nodeOffset - 1);
while (brContent && brContent->IsHTMLElement()) {
if (!brContent->IsHTMLElement(nsGkAtoms::br) || IsContentBR(brContent)) {
break;
}
brContent = node->GetChildAt(--nodeOffset - 1);
// When the offset is at the end of the text node, set it to after the
// text node, to make sure the caret is drawn on a new line when the last
// character of the text node is '\n' in <textarea>.
int32_t textLength =
static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength());
MOZ_ASSERT(nodeOffset <= textLength, "Offset is past length of text node");
if (nodeOffset != textLength) {
return;
}
*aNode = node;
*aNodeOffset = std::max(nodeOffset, 0);
nsIContent* aRootParent = aRoot->GetParent();
if (NS_WARN_IF(!aRootParent)) {
return;
}
// If the root node is not an anonymous div of <textarea>, we don't need to
// do this hack. If you did this, ContentEventHandler couldn't distinguish
// if the range includes open tag of the next node in some cases, e.g.,
// textNode]<p></p> vs. textNode<p>]</p>
if (!aRootParent->IsHTMLElement(nsGkAtoms::textarea)) {
return;
}
*aNode = node->GetParent();
MOZ_ASSERT((*aNode)->IndexOf(node) != -1);
*aNodeOffset = (*aNode)->IndexOf(node) + 1;
}
nsresult

View File

@ -10,6 +10,8 @@
#include "mozilla/EventForwards.h"
#include "mozilla/dom/Selection.h"
#include "nsCOMPtr.h"
#include "nsIFrame.h"
#include "nsINode.h"
#include "nsRange.h"
class nsPresContext;
@ -81,35 +83,161 @@ public:
// FlatText means the text that is generated from DOM tree. The BR elements
// are replaced to native linefeeds. Other elements are ignored.
// Get the offset in FlatText of the range. (also used by IMEContentObserver)
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsINode* aNode,
int32_t aNodeOffset,
uint32_t* aOffset,
LineBreakType aLineBreakType);
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsRange* aRange,
uint32_t* aOffset,
LineBreakType aLineBreakType);
// NodePosition stores a pair of node and offset in the node.
// When mNode is an element and mOffset is 0, the start position means after
// the open tag of mNode.
// This is useful to receive one or more sets of them instead of nsRange.
struct NodePosition
{
nsCOMPtr<nsINode> mNode;
int32_t mOffset;
// Only when mNode is an element node and mOffset is 0, mAfterOpenTag is
// referred.
bool mAfterOpenTag;
NodePosition()
: mOffset(-1)
, mAfterOpenTag(true)
{
}
NodePosition(nsINode* aNode, int32_t aOffset)
: mNode(aNode)
, mOffset(aOffset)
, mAfterOpenTag(true)
{
}
explicit NodePosition(const nsIFrame::ContentOffsets& aContentOffsets)
: mNode(aContentOffsets.content)
, mOffset(aContentOffsets.offset)
, mAfterOpenTag(true)
{
}
protected:
NodePosition(nsINode* aNode, int32_t aOffset, bool aAfterOpenTag)
: mNode(aNode)
, mOffset(aOffset)
, mAfterOpenTag(aAfterOpenTag)
{
}
public:
bool operator==(const NodePosition& aOther) const
{
return mNode == aOther.mNode &&
mOffset == aOther.mOffset &&
mAfterOpenTag == aOther.mAfterOpenTag;
}
bool IsValid() const
{
return mNode && mOffset >= 0;
}
bool OffsetIsValid() const
{
return IsValid() && static_cast<uint32_t>(mOffset) <= mNode->Length();
}
bool IsBeforeOpenTag() const
{
return IsValid() && mNode->IsElement() && !mOffset && !mAfterOpenTag;
}
bool IsImmediatelyAfterOpenTag() const
{
return IsValid() && mNode->IsElement() && !mOffset && mAfterOpenTag;
}
nsresult SetToRangeStart(nsRange* aRange) const
{
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mNode));
return aRange->SetStart(domNode, mOffset);
}
nsresult SetToRangeEnd(nsRange* aRange) const
{
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mNode));
return aRange->SetEnd(domNode, mOffset);
}
nsresult SetToRangeEndAfter(nsRange* aRange) const
{
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mNode));
return aRange->SetEndAfter(domNode);
}
};
// NodePositionBefore isn't good name if mNode isn't an element node nor
// mOffset is not 0, though, when mNode is an element node and mOffset is 0,
// this is treated as before the open tag of mNode.
struct NodePositionBefore final : public NodePosition
{
NodePositionBefore(nsINode* aNode, int32_t aOffset)
: NodePosition(aNode, aOffset, false)
{
}
};
// Get the flatten text length in the range.
// @param aStartPosition Start node and offset in the node of the range.
// @param aEndPosition End node and offset in the node of the range.
// @param aRootContent The root content of the editor or document.
// aRootContent won't cause any text including
// line breaks.
// @param aLength The result of the flatten text length of the
// range.
// @param aLineBreakType Whether this computes flatten text length with
// native line breakers on the platform or
// with XP line breaker (\n).
// @param aIsRemovingNode Should be true only when this is called from
// nsIMutationObserver::ContentRemoved().
// When this is true, aStartPosition.mNode should
// be the root node of removing nodes and mOffset
// should be 0 and aEndPosition.mNode should be
// same as aStartPosition.mNode and mOffset should
// be number of the children of mNode.
static nsresult GetFlatTextLengthInRange(const NodePosition& aStartPosition,
const NodePosition& aEndPosition,
nsIContent* aRootContent,
uint32_t* aLength,
LineBreakType aLineBreakType,
bool aIsRemovingNode = false);
// Computes the native text length between aStartOffset and aEndOffset of
// aContent. Currently, this method supports only text node or br element
// for aContent.
// aContent. aContent must be a text node.
static uint32_t GetNativeTextLength(nsIContent* aContent,
uint32_t aStartOffset,
uint32_t aEndOffset);
// Get the native text length of a content node excluding any children
// Get the native text length of aContent. aContent must be a text node.
static uint32_t GetNativeTextLength(nsIContent* aContent,
uint32_t aMaxLength = UINT32_MAX);
// Get the native text length which is inserted before aContent.
// aContent should be an element.
static uint32_t GetNativeTextLengthBefore(nsIContent* aContent,
nsINode* aRootNode);
protected:
// Get the text length of aContent. aContent must be a text node.
static uint32_t GetTextLength(nsIContent* aContent,
LineBreakType aLineBreakType,
uint32_t aMaxLength = UINT32_MAX);
// Get the text length of a given range of a content node in
// the given line break type.
static uint32_t GetTextLengthInRange(nsIContent* aContent,
uint32_t aXPStartOffset,
uint32_t aXPEndOffset,
LineBreakType aLineBreakType);
protected:
static uint32_t GetTextLength(nsIContent* aContent,
LineBreakType aLineBreakType,
uint32_t aMaxLength = UINT32_MAX);
// Get the contents of aRange as plain text.
nsresult GenerateFlatTextContent(nsRange* aRange,
nsAFlatString& aString,
LineBreakType aLineBreakType);
// Get the text length before the start position of aRange.
nsresult GetFlatTextLengthBefore(nsRange* aRange,
uint32_t* aOffset,
LineBreakType aLineBreakType);
// Check if we should insert a line break before aContent.
// This should return false only when aContent is an html element which
// is typically used in a paragraph like <em>.
static bool ShouldBreakLineBefore(nsIContent* aContent,
nsINode* aRootNode);
// Get the line breaker length.
static inline uint32_t GetBRLength(LineBreakType aLineBreakType);
static LineBreakType GetLineBreakType(WidgetQueryContentEvent* aEvent);
static LineBreakType GetLineBreakType(WidgetSelectionEvent* aEvent);
static LineBreakType GetLineBreakType(bool aUseNativeLineBreak);
@ -152,10 +280,10 @@ protected:
int32_t aXPStartOffset,
int32_t aXPEndOffset,
LineBreakType aLineBreakType);
static nsresult GenerateFlatFontRanges(nsRange* aRange,
FontRangeArray& aFontRanges,
uint32_t& aLength,
LineBreakType aLineBreakType);
nsresult GenerateFlatFontRanges(nsRange* aRange,
FontRangeArray& aFontRanges,
uint32_t& aLength,
LineBreakType aLineBreakType);
};
} // namespace mozilla

View File

@ -5716,14 +5716,16 @@ EventStateManager::WheelPrefs::NeedToComputeLineOrPageDelta(
(mMultiplierY[index] != 1.0 && mMultiplierY[index] != -1.0);
}
bool
EventStateManager::WheelPrefs::HasUserPrefsForDelta(WidgetWheelEvent* aEvent)
void
EventStateManager::WheelPrefs::GetUserPrefsForEvent(WidgetWheelEvent* aEvent,
double* aOutMultiplierX,
double* aOutMultiplierY)
{
Index index = GetIndexFor(aEvent);
Init(index);
return mMultiplierX[index] != 1.0 ||
mMultiplierY[index] != 1.0;
*aOutMultiplierX = mMultiplierX[index];
*aOutMultiplierY = mMultiplierY[index];
}
bool
@ -5733,10 +5735,13 @@ EventStateManager::WheelEventIsScrollAction(WidgetWheelEvent* aEvent)
WheelPrefs::GetInstance()->ComputeActionFor(aEvent) == WheelPrefs::ACTION_SCROLL;
}
bool
EventStateManager::WheelEventNeedsDeltaMultipliers(WidgetWheelEvent* aEvent)
void
EventStateManager::GetUserPrefsForWheelEvent(WidgetWheelEvent* aEvent,
double* aOutMultiplierX,
double* aOutMultiplierY)
{
return WheelPrefs::GetInstance()->HasUserPrefsForDelta(aEvent);
WheelPrefs::GetInstance()->GetUserPrefsForEvent(
aEvent, aOutMultiplierX, aOutMultiplierY);
}
bool

View File

@ -242,9 +242,10 @@ public:
// Returns true if the given WidgetWheelEvent will resolve to a scroll action.
static bool WheelEventIsScrollAction(WidgetWheelEvent* aEvent);
// Returns true if user prefs for wheel deltas apply to the given
// WidgetWheelEvent.
static bool WheelEventNeedsDeltaMultipliers(WidgetWheelEvent* aEvent);
// Returns user-set multipliers for a wheel event.
static void GetUserPrefsForWheelEvent(WidgetWheelEvent* aEvent,
double* aOutMultiplierX,
double* aOutMultiplierY);
// Returns whether or not a frame can be vertically scrolled with a mouse
// wheel (as opposed to, say, a selection or touch scroll).
@ -449,7 +450,9 @@ protected:
* Returns whether or not ApplyUserPrefsToDelta() would change the delta
* values of an event.
*/
bool HasUserPrefsForDelta(WidgetWheelEvent* aEvent);
void GetUserPrefsForEvent(WidgetWheelEvent* aEvent,
double* aOutMultiplierX,
double* aOutMultiplierY);
/**
* If ApplyUserPrefsToDelta() changed the delta values with customized

View File

@ -876,11 +876,13 @@ IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
uint32_t offset = 0;
// get offsets of change and fire notification
nsresult rv =
ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aContent,
aInfo->mChangeStart,
&offset,
LINE_BREAK_TYPE_NATIVE);
NS_ENSURE_SUCCESS_VOID(rv);
ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
NodePosition(aContent, aInfo->mChangeStart),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
uint32_t newLength =
ContentEventHandler::GetNativeTextLength(aContent, aInfo->mChangeStart,
@ -912,9 +914,10 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
nsresult rv = NS_OK;
if (!mEndOfAddedTextCache.Match(aContainer, aStartIndex)) {
mEndOfAddedTextCache.Clear();
rv = ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aContainer,
aStartIndex, &offset,
LINE_BREAK_TYPE_NATIVE);
rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
NodePositionBefore(aContainer, aStartIndex),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED((rv)))) {
return;
}
@ -923,11 +926,12 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
}
// get offset at the end of the last added node
nsIContent* childAtStart = aContainer->GetChildAt(aStartIndex);
uint32_t addingLength = 0;
rv = ContentEventHandler::GetFlatTextOffsetOfRange(childAtStart, aContainer,
aEndIndex, &addingLength,
LINE_BREAK_TYPE_NATIVE);
rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePositionBefore(aContainer, aStartIndex),
NodePosition(aContainer, aEndIndex),
mRootContent, &addingLength,
LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED((rv)))) {
mEndOfAddedTextCache.Clear();
return;
@ -988,10 +992,12 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
uint32_t offset = 0;
nsresult rv = NS_OK;
if (!mStartOfRemovingTextRangeCache.Match(containerNode, aIndexInContainer)) {
rv =
ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, containerNode,
aIndexInContainer, &offset,
LINE_BREAK_TYPE_NATIVE);
// At removing a child node of aContainer, we need the line break caused
// by open tag of aContainer. Be careful when aIndexInContainer is 0.
rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
NodePosition(containerNode, aIndexInContainer),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) {
mStartOfRemovingTextRangeCache.Clear();
return;
@ -1003,18 +1009,20 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
}
// get offset at the end of the deleted node
int32_t nodeLength =
aChild->IsNodeOfType(nsINode::eTEXT) ?
static_cast<int32_t>(aChild->TextLength()) :
std::max(static_cast<int32_t>(aChild->GetChildCount()), 1);
MOZ_ASSERT(nodeLength >= 0, "The node length is out of range");
uint32_t textLength = 0;
rv = ContentEventHandler::GetFlatTextOffsetOfRange(aChild, aChild,
nodeLength, &textLength,
LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) {
mStartOfRemovingTextRangeCache.Clear();
return;
if (aChild->IsNodeOfType(nsINode::eTEXT)) {
textLength = ContentEventHandler::GetNativeTextLength(aChild);
} else {
uint32_t nodeLength = static_cast<int32_t>(aChild->GetChildCount());
rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePositionBefore(aChild, 0),
NodePosition(aChild, nodeLength),
mRootContent, &textLength,
LINE_BREAK_TYPE_NATIVE, true);
if (NS_WARN_IF(NS_FAILED(rv))) {
mStartOfRemovingTextRangeCache.Clear();
return;
}
}
if (!textLength) {
@ -1026,16 +1034,6 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
MaybeNotifyIMEOfTextChange(data);
}
static nsIContent*
GetContentBR(dom::Element* aElement)
{
if (!aElement->IsNodeOfType(nsINode::eCONTENT)) {
return nullptr;
}
nsIContent* content = static_cast<nsIContent*>(aElement);
return content->IsHTMLElement(nsGkAtoms::br) ? content : nullptr;
}
void
IMEContentObserver::AttributeWillChange(nsIDocument* aDocument,
dom::Element* aElement,
@ -1044,9 +1042,8 @@ IMEContentObserver::AttributeWillChange(nsIDocument* aDocument,
int32_t aModType,
const nsAttrValue* aNewValue)
{
nsIContent *content = GetContentBR(aElement);
mPreAttrChangeLength = content ?
ContentEventHandler::GetNativeTextLength(content) : 0;
mPreAttrChangeLength =
ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
}
void
@ -1066,22 +1063,20 @@ IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
return;
}
nsIContent *content = GetContentBR(aElement);
if (!content) {
return;
}
uint32_t postAttrChangeLength =
ContentEventHandler::GetNativeTextLength(content);
ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
if (postAttrChangeLength == mPreAttrChangeLength) {
return;
}
uint32_t start;
nsresult rv =
ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, content,
0, &start,
LINE_BREAK_TYPE_NATIVE);
NS_ENSURE_SUCCESS_VOID(rv);
ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
NodePositionBefore(aElement, 0),
mRootContent, &start, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
TextChangeData data(start, start + mPreAttrChangeLength,
start + postAttrChangeLength, causedByComposition,

View File

@ -40,6 +40,8 @@ class IMEContentObserver final : public nsISelectionListener
, public nsIEditorObserver
{
public:
typedef ContentEventHandler::NodePosition NodePosition;
typedef ContentEventHandler::NodePositionBefore NodePositionBefore;
typedef widget::IMENotification::SelectionChangeData SelectionChangeData;
typedef widget::IMENotification::TextChangeData TextChangeData;
typedef widget::IMENotification::TextChangeDataBase TextChangeDataBase;

View File

@ -21,6 +21,7 @@
#include "nsPresContext.h"
#include "prtime.h"
#include "Units.h"
#include "AsyncScrollBase.h"
namespace mozilla {
@ -137,7 +138,7 @@ WheelTransaction::UpdateTransaction(WidgetWheelEvent* aEvent)
SetTimeout();
if (sScrollSeriesCounter != 0 && OutOfTime(sTime, kScrollSeriesTimeout)) {
if (sScrollSeriesCounter != 0 && OutOfTime(sTime, kScrollSeriesTimeoutMs)) {
sScrollSeriesCounter = 0;
}
sScrollSeriesCounter++;
@ -383,14 +384,9 @@ WheelTransaction::AccelerateWheelDelta(WidgetWheelEvent* aEvent,
}
/* static */ double
WheelTransaction::ComputeAcceleratedWheelDelta(double aDelta,
int32_t aFactor)
WheelTransaction::ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor)
{
if (aDelta == 0.0) {
return 0;
}
return (aDelta * sScrollSeriesCounter * (double)aFactor / 10);
return mozilla::ComputeAcceleratedWheelDelta(aDelta, sScrollSeriesCounter, aFactor);
}
/* static */ int32_t

View File

@ -151,8 +151,6 @@ public:
bool aAllowScrollSpeedOverride);
protected:
static const uint32_t kScrollSeriesTimeout = 80; // in milliseconds
static void BeginTransaction(nsIFrame* aTargetFrame,
WidgetWheelEvent* aEvent);
// Be careful, UpdateTransaction may fire a DOM event, therefore, the target

View File

@ -158,9 +158,7 @@ HTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIDocument* doc = GetComposedDoc();
if (doc) {
doc->RegisterPendingLinkUpdate(this);
if (nsHTMLDNSPrefetch::IsAllowed(OwnerDoc())) {
nsHTMLDNSPrefetch::PrefetchLow(this);
}
TryDNSPrefetch();
}
return rv;
@ -172,18 +170,9 @@ HTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent)
// Cancel any DNS prefetches
// Note: Must come before ResetLinkState. If called after, it will recreate
// mCachedURI based on data that is invalid - due to a call to GetHostname.
CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED,
HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
// If prefetch was deferred, clear flag and move on
if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED))
UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
// Else if prefetch was requested, clear flag and send cancellation
else if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_REQUESTED)) {
UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
// Possible that hostname could have changed since binding, but since this
// covers common cases, most DNS prefetch requests will be canceled
nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT);
}
// If this link is ever reinserted into a document, it might
// be under a different xml:base, so forget the cached state now.
Link::ResetLinkState(false, Link::ElementHasHref());
@ -406,6 +395,10 @@ HTMLAnchorElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
reset = true;
}
}
if (reset) {
CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED,
HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
}
}
nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
@ -418,6 +411,9 @@ HTMLAnchorElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
// to get updated information about the visitedness from Link.
if (reset) {
Link::ResetLinkState(!!aNotify, true);
if (IsInComposedDoc()) {
TryDNSPrefetch();
}
}
return rv;
@ -427,6 +423,14 @@ nsresult
HTMLAnchorElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify)
{
bool href =
(aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID);
if (href) {
CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED,
HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
}
nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute,
aNotify);
@ -435,7 +439,7 @@ HTMLAnchorElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
// we will need the updated attribute value because notifying the document
// that content states have changed will call IntrinsicState, which will try
// to get updated information about the visitedness from Link.
if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
if (href) {
Link::ResetLinkState(!!aNotify, false);
}

View File

@ -28,6 +28,22 @@
#include "nsStyleConsts.h"
#include "nsUnicharUtils.h"
#define LINK_ELEMENT_FLAG_BIT(n_) \
NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
// Link element specific bits
enum {
// Indicates that a DNS Prefetch has been requested from this Link element.
HTML_LINK_DNS_PREFETCH_REQUESTED = LINK_ELEMENT_FLAG_BIT(0),
// Indicates that a DNS Prefetch was added to the deferral queue
HTML_LINK_DNS_PREFETCH_DEFERRED = LINK_ELEMENT_FLAG_BIT(1)
};
#undef LINK_ELEMENT_FLAG_BIT
ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
NS_IMPL_NS_NEW_HTML_ELEMENT(Link)
namespace mozilla {
@ -126,6 +142,26 @@ HTMLLinkElement::SetItemValueText(const nsAString& aValue)
SetHref(aValue);
}
void
HTMLLinkElement::OnDNSPrefetchRequested()
{
UnsetFlags(HTML_LINK_DNS_PREFETCH_DEFERRED);
SetFlags(HTML_LINK_DNS_PREFETCH_REQUESTED);
}
void
HTMLLinkElement::OnDNSPrefetchDeferred()
{
UnsetFlags(HTML_LINK_DNS_PREFETCH_REQUESTED);
SetFlags(HTML_LINK_DNS_PREFETCH_DEFERRED);
}
bool
HTMLLinkElement::HasDeferredDNSPrefetchRequest()
{
return HasFlag(HTML_LINK_DNS_PREFETCH_DEFERRED);
}
nsresult
HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
@ -145,6 +181,9 @@ HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
if (IsInComposedDoc()) {
UpdatePreconnect();
if (HasDNSPrefetchRel()) {
TryDNSPrefetch();
}
}
void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal;
@ -173,6 +212,12 @@ HTMLLinkElement::LinkRemoved()
void
HTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
// Cancel any DNS prefetches
// Note: Must come before ResetLinkState. If called after, it will recreate
// mCachedURI based on data that is invalid - due to a call to GetHostname.
CancelDNSPrefetch(HTML_LINK_DNS_PREFETCH_DEFERRED,
HTML_LINK_DNS_PREFETCH_REQUESTED);
// If this link is ever reinserted into a document, it might
// be under a different xml:base, so forget the cached state now.
Link::ResetLinkState(false, Link::ElementHasHref());
@ -322,6 +367,32 @@ HTMLLinkElement::UpdatePreconnect()
}
}
bool
HTMLLinkElement::HasDNSPrefetchRel()
{
nsAutoString rel;
if (GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) {
return !!(ParseLinkTypes(rel, NodePrincipal()) &
nsStyleLinkElement::eDNS_PREFETCH);
}
return false;
}
nsresult
HTMLLinkElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsAttrValueOrString* aValue, bool aNotify)
{
if (aNameSpaceID == kNameSpaceID_None &&
(aName == nsGkAtoms::href || aName == nsGkAtoms::rel)) {
CancelDNSPrefetch(HTML_LINK_DNS_PREFETCH_DEFERRED,
HTML_LINK_DNS_PREFETCH_REQUESTED);
}
return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName,
aValue, aNotify);
}
nsresult
HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify)
@ -368,6 +439,11 @@ HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
}
}
if ((aName == nsGkAtoms::rel || aName == nsGkAtoms::href) &&
HasDNSPrefetchRel() && IsInComposedDoc()) {
TryDNSPrefetch();
}
UpdateStyleSheetInternal(nullptr, nullptr,
dropSheet ||
(aName == nsGkAtoms::title ||

View File

@ -61,6 +61,9 @@ public:
bool aCompileEventHandlers) override;
virtual void UnbindFromTree(bool aDeep = true,
bool aNullParent = true) override;
virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsAttrValueOrString* aValue,
bool aNotify) override;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue,
bool aNotify) override;
@ -77,6 +80,10 @@ public:
void CreateAndDispatchEvent(nsIDocument* aDoc, const nsAString& aEventName);
virtual void OnDNSPrefetchDeferred() override;
virtual void OnDNSPrefetchRequested() override;
virtual bool HasDeferredDNSPrefetchRequest() override;
// WebIDL
bool Disabled();
void SetDisabled(bool aDisabled);
@ -166,6 +173,9 @@ protected:
// nsGenericHTMLElement
virtual void GetItemValueText(DOMString& text) override;
virtual void SetItemValueText(const nsAString& text) override;
bool HasDNSPrefetchRel();
RefPtr<nsDOMTokenList > mRelList;
private:
RefPtr<ImportLoader> mImportLoader;

View File

@ -3422,15 +3422,7 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
#endif // MOZ_EME
}
// If this element had a video track, but consists only of an audio track now,
// delete the VideoFrameContainer. This happens when the src is changed to an
// audio only file.
// Else update its dimensions.
if (!aInfo->HasVideo()) {
ResetState();
} else {
mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
}
mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
if (IsVideo() && aInfo->HasVideo()) {
// We are a video element playing video so update the screen wakelock

View File

@ -225,18 +225,17 @@ HTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions,
int32_t aDepth,
bool aNotify)
{
MOZ_ASSERT(aDepth == 0 || aDepth == 1);
int32_t insertIndex = aListIndex;
HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions);
if (optElement) {
mOptions->InsertOptionAt(optElement, insertIndex);
insertIndex++;
} else {
} else if (aDepth == 0) {
// If it's at the top level, then we just found out there are non-options
// at the top level, which will throw off the insert count
if (aDepth == 0) {
mNonOptionChildren++;
}
mNonOptionChildren++;
// Deal with optgroups
if (aOptions->IsHTMLElement(nsGkAtoms::optgroup)) {
@ -252,7 +251,7 @@ HTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions,
}
}
}
}
} // else ignore even if optgroup; we want to ignore nested optgroups.
// Deal with the selected list
if (insertIndex - aListIndex) {
@ -307,6 +306,7 @@ HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions,
int32_t aDepth,
bool aNotify)
{
MOZ_ASSERT(aDepth == 0 || aDepth == 1);
int32_t numRemoved = 0;
HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions);
@ -317,11 +317,9 @@ HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions,
}
mOptions->RemoveOptionAt(aListIndex);
numRemoved++;
} else {
} else if (aDepth == 0) {
// Yay, one less artifact at the top level.
if (aDepth == 0) {
mNonOptionChildren--;
}
mNonOptionChildren--;
// Recurse down deeper for options
if (mOptGroupCount && aOptions->IsHTMLElement(nsGkAtoms::optgroup)) {
@ -341,7 +339,7 @@ HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions,
}
}
}
}
} // else don't check for an optgroup; we want to ignore nested optgroups
if (numRemoved) {
// Tell the widget we removed the options

View File

@ -14,6 +14,9 @@ NS_IMPL_NS_NEW_HTML_ELEMENT(Unknown)
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS_INHERITED(HTMLUnknownElement, nsGenericHTMLElement,
HTMLUnknownElement)
JSObject*
HTMLUnknownElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
{

View File

@ -12,9 +12,17 @@
namespace mozilla {
namespace dom {
#define NS_HTMLUNKNOWNELEMENT_IID \
{ 0xc09e665b, 0x3876, 0x40dd, \
{ 0x85, 0x28, 0x44, 0xc2, 0x3f, 0xd4, 0x58, 0xf2 } }
class HTMLUnknownElement final : public nsGenericHTMLElement
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_HTMLUNKNOWNELEMENT_IID)
NS_DECL_ISUPPORTS_INHERITED
explicit HTMLUnknownElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
: nsGenericHTMLElement(aNodeInfo)
{
@ -26,9 +34,12 @@ public:
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
protected:
virtual ~HTMLUnknownElement() {}
virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
};
NS_DEFINE_STATIC_IID_ACCESSOR(HTMLUnknownElement, NS_HTMLUNKNOWNELEMENT_IID)
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<script>
function boom()
{
var a = document.createElement("select");
var f = document.createElement("optgroup");
var g = document.createElement("optgroup");
a.appendChild(f);
g.appendChild(document.createElement("option"));
f.appendChild(g);
a.appendChild(document.createElement("option"));
document.body.appendChild(a);
}
</script>
</head>
<body onload="boom();"></body>
</html>

View File

@ -73,4 +73,5 @@ load 903106.html
load 916322-1.html
load 916322-2.html
load 1032654.html
pref(dom.image.srcset.enabled,true) load 1141260.html
pref(dom.image.srcset.enabled,true) load 1141260.html
load 1228876.html

View File

@ -13,22 +13,31 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=741266
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=741266">Mozilla Bug 741266</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 741266 **/
var w = window.open("", "", "width=100,height=100");
is(w.innerHeight, 100, "Popup height should be 100 when opened with window.open");
// XXXbz On at least some platforms, the innerWidth is off by the scrollbar
// width for some reason. So just make sure it's the same for both popups.
var width = w.innerWidth;
w.close();
w = document.open("", "", "width=100,height=100");
is(w.innerHeight, 100, "Popup height should be 100 when opened with document.open");
is(w.innerWidth, width, "Popup width should be the same when opened with document.open");
w.close();
SimpleTest.waitForExplicitFinish();
var url = URL.createObjectURL(new Blob([""], { type: "text/html" }));
var w = window.open(url, "", "width=100,height=100");
w.onload = function() {
is(w.innerHeight, 100, "Popup height should be 100 when opened with window.open");
// XXXbz On at least some platforms, the innerWidth is off by the scrollbar
// width for some reason. So just make sure it's the same for both popups.
var width = w.innerWidth;
w.close();
w = document.open(url, "", "width=100,height=100");
w.onload = function() {
is(w.innerHeight, 100, "Popup height should be 100 when opened with document.open");
is(w.innerWidth, width, "Popup width should be the same when opened with document.open");
w.close();
SimpleTest.finish();
};
};
</script>
</pre>
</body>

View File

@ -1808,10 +1808,11 @@ TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
event.widget = mPuppetWidget;
APZCCallbackHelper::DispatchWidgetEvent(event);
if (event.mCanTriggerSwipe) {
SendRespondStartSwipeEvent(aInputBlockId, event.TriggersSwipe());
}
if (aEvent.mFlags.mHandledByAPZ) {
if (event.mCanTriggerSwipe) {
SendRespondStartSwipeEvent(aInputBlockId, event.TriggersSwipe());
}
mAPZEventState->ProcessWheelEvent(event, aGuid, aInputBlockId);
}
return true;

View File

@ -2694,6 +2694,12 @@ TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId,
nsEventStatus* aOutApzResponse)
{
// Let the widget know that the event will be sent to the child process,
// which will (hopefully) send a confirmation notice back to APZ.
// Do this even if APZ is off since we need it for swipe gesture support on
// OS X without APZ.
InputAPZContext::SetRoutedToChildProcess();
if (AsyncPanZoomEnabled()) {
if (aOutTargetGuid) {
*aOutTargetGuid = InputAPZContext::GetTargetLayerGuid();
@ -2715,10 +2721,6 @@ TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
if (aOutApzResponse) {
*aOutApzResponse = InputAPZContext::GetApzResponse();
}
// Let the widget know that the event will be sent to the child process,
// which will (hopefully) send a confirmation notice back to APZ.
InputAPZContext::SetRoutedToChildProcess();
} else {
if (aOutInputBlockId) {
*aOutInputBlockId = 0;

View File

@ -85,16 +85,6 @@ public:
NS_DispatchToMainThread(r);
}
// Set the media as being seekable or not.
virtual void SetMediaSeekable(bool aMediaSeekable) = 0;
void DispatchSetMediaSeekable(bool aMediaSeekable)
{
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<bool>(
this, &AbstractMediaDecoder::SetMediaSeekable, aMediaSeekable);
NS_DispatchToMainThread(r);
}
virtual VideoFrameContainer* GetVideoFrameContainer() = 0;
virtual mozilla::layers::ImageContainer* GetImageContainer() = 0;

View File

@ -135,7 +135,7 @@ private:
uint32_t mLength;
uint32_t mOffset;
nsCOMPtr<nsIPrincipal> mPrincipal;
const nsAutoCString mContentType;
const nsCString mContentType;
};
} // namespace mozilla

View File

@ -308,7 +308,7 @@ ParseXing(const char *aBuffer)
}
static int64_t
FindNumVBRFrames(const nsAutoCString& aFrame)
FindNumVBRFrames(const nsCString& aFrame)
{
const char *buffer = aFrame.get();
const char *bufferEnd = aFrame.get() + aFrame.Length();

View File

@ -198,7 +198,7 @@ private:
// If the MP3 has a variable bitrate, then there *should* be metadata about
// the encoding in the first frame. We buffer the first frame here.
nsAutoCString mFirstFrame;
nsCString mFirstFrame;
// While we are reading the first frame, this is the stream offset of the
// last byte of that frame. -1 at all other times.

View File

@ -617,6 +617,7 @@ MediaDecoder::Shutdown()
mFirstFrameLoadedListener.Disconnect();
mOnPlaybackEvent.Disconnect();
mOnSeekingStart.Disconnect();
mOnMediaNotSeekable.Disconnect();
}
// Force any outstanding seek and byterange requests to complete
@ -728,6 +729,8 @@ MediaDecoder::SetStateMachineParameters()
AbstractThread::MainThread(), this, &MediaDecoder::OnPlaybackEvent);
mOnSeekingStart = mDecoderStateMachine->OnSeekingStart().Connect(
AbstractThread::MainThread(), this, &MediaDecoder::SeekingStarted);
mOnMediaNotSeekable = mDecoderStateMachine->OnMediaNotSeekable().Connect(
AbstractThread::MainThread(), this, &MediaDecoder::OnMediaNotSeekable);
}
void
@ -841,6 +844,7 @@ MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
aInfo->mAudio.mChannels, aInfo->mAudio.mRate,
aInfo->HasAudio(), aInfo->HasVideo());
SetMediaSeekable(aInfo->mMediaSeekable);
mInfo = aInfo.forget();
ConstructMediaTracks();

View File

@ -471,7 +471,7 @@ public:
virtual void SetElementVisibility(bool aIsVisible) {}
// Set a flag indicating whether seeking is supported
virtual void SetMediaSeekable(bool aMediaSeekable) override;
void SetMediaSeekable(bool aMediaSeekable);
// Returns true if this media supports seeking. False for example for WebM
// files without an index and chained ogg files.
@ -804,6 +804,11 @@ private:
void OnPlaybackEvent(MediaEventType aEvent);
void OnMediaNotSeekable()
{
SetMediaSeekable(false);
}
MediaEventProducer<void> mDataArrivedEvent;
// The state machine object for handling the decoding. It is safe to
@ -927,6 +932,7 @@ protected:
MediaEventListener mOnPlaybackEvent;
MediaEventListener mOnSeekingStart;
MediaEventListener mOnMediaNotSeekable;
protected:
// Whether the state machine is shut down.

View File

@ -239,10 +239,6 @@ public:
return &mBuffered;
}
// Indicates if the media is seekable.
// ReadMetada should be called before calling this method.
virtual bool IsMediaSeekable() = 0;
void DispatchSetStartTime(int64_t aStartTime)
{
RefPtr<MediaDecoderReader> self = this;
@ -279,6 +275,9 @@ public:
return mTimedMetadataEvent;
}
// Notified by the OggReader during playback when chained ogg is detected.
MediaEventSource<void>& OnMediaNotSeekable() { return mOnMediaNotSeekable; }
protected:
virtual ~MediaDecoderReader();
@ -369,6 +368,9 @@ protected:
// Used to send TimedMetadata to the listener.
TimedMetadataEventProducer mTimedMetadataEvent;
// Notify if this media is not seekable.
MediaEventProducer<void> mOnMediaNotSeekable;
private:
// Does any spinup that needs to happen on this task queue. This runs on a
// different thread than Init, and there should not be ordering dependencies

View File

@ -1909,7 +1909,6 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
// Set mode to PLAYBACK after reading metadata.
mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
mDecoder->DispatchSetMediaSeekable(mReader->IsMediaSeekable());
mInfo = aMetadata->mInfo;
mMetadataTags = aMetadata->mTags.forget();
RefPtr<MediaDecoderStateMachine> self = this;

View File

@ -232,6 +232,10 @@ public:
return mMetadataManager.TimedMetadataEvent();
}
MediaEventSource<void>& OnMediaNotSeekable() {
return mReader->OnMediaNotSeekable();
}
MediaEventSourceExc<nsAutoPtr<MediaInfo>,
nsAutoPtr<MetadataTags>,
MediaDecoderEventVisibility>&

View File

@ -65,7 +65,6 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
, mLastReportedNumDecodedFrames(0)
, mLayersBackendType(aLayersBackend)
, mInitDone(false)
, mSeekable(false)
, mIsEncrypted(false)
, mTrackDemuxersMayBlock(false)
, mHardwareAccelerationDisabled(false)
@ -324,7 +323,7 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(duration));
}
mSeekable = mDemuxer->IsSeekable();
mInfo.mMediaSeekable = mDemuxer->IsSeekable();
if (!videoActive && !audioActive) {
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
@ -1331,7 +1330,7 @@ MediaFormatReader::Seek(int64_t aTime, int64_t aUnused)
MOZ_DIAGNOSTIC_ASSERT(mVideo.mTimeThreshold.isNothing());
MOZ_DIAGNOSTIC_ASSERT(mAudio.mTimeThreshold.isNothing());
if (!mSeekable) {
if (!mInfo.mMediaSeekable) {
LOG("Seek() END (Unseekable)");
return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}

View File

@ -49,11 +49,6 @@ public:
RefPtr<SeekPromise>
Seek(int64_t aTime, int64_t aUnused) override;
bool IsMediaSeekable() override
{
return mSeekable;
}
protected:
void NotifyDataArrivedInternal() override;
@ -396,9 +391,6 @@ private:
// True if we've read the streams' metadata.
bool mInitDone;
MozPromiseHolder<MetadataPromise> mMetadataPromise;
// Accessed from multiple thread, in particular the MediaDecoderStateMachine,
// however the value doesn't change after reading the metadata.
bool mSeekable;
bool IsEncrypted()
{
return mIsEncrypted;

View File

@ -75,7 +75,7 @@ public:
TrackID mTrackId;
nsAutoCString mMimeType;
nsCString mMimeType;
int64_t mDuration;
int64_t mMediaTime;
CryptoTrack mCrypto;
@ -400,6 +400,9 @@ public:
// a duration until we know the start time, so we need to track it separately.
media::NullableTimeUnit mUnadjustedMetadataEndTime;
// True if the media is seekable (i.e. supports random access).
bool mMediaSeekable = true;
EncryptionInfo mCrypto;
};
@ -451,7 +454,7 @@ private:
uint32_t mStreamSourceID;
public:
const nsAutoCString& mMimeType;
const nsCString& mMimeType;
};
} // namespace mozilla

View File

@ -451,10 +451,10 @@ protected:
// Content-Type of the channel. This is copied from the nsIChannel when the
// MediaResource is created. This is constant, so accessing from any thread
// is safe.
const nsAutoCString mContentType;
const nsCString mContentType;
// Copy of the url of the channel resource.
nsAutoCString mContentURL;
nsCString mContentURL;
// True if SetLoadInBackground() has been called with
// aLoadInBackground = true, i.e. when the document load event is not

View File

@ -453,7 +453,7 @@ void
LogToBrowserConsole(const nsAString& aMsg)
{
if (!NS_IsMainThread()) {
nsAutoString msg(aMsg);
nsString msg(aMsg);
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableFunction([msg]() { LogToBrowserConsole(msg); });
NS_DispatchToMainThread(task.forget(), NS_DISPATCH_NORMAL);

View File

@ -48,12 +48,6 @@ public:
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold);
virtual bool IsMediaSeekable()
{
// not used
return true;
}
virtual nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags);
virtual RefPtr<SeekPromise>

View File

@ -190,6 +190,7 @@ DirectShowReader::ReadMetadata(MediaInfo* aInfo,
DWORD seekCaps = 0;
hr = mMediaSeeking->GetCapabilities(&seekCaps);
mInfo.mMediaSeekable = SUCCEEDED(hr) && (AM_SEEKING_CanSeekAbsolute & seekCaps);
int64_t duration = mMP3FrameParser.GetDuration();
if (SUCCEEDED(hr)) {
@ -210,15 +211,6 @@ DirectShowReader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
bool
DirectShowReader::IsMediaSeekable()
{
DWORD seekCaps = 0;
HRESULT hr = mMediaSeeking->GetCapabilities(&seekCaps);
return ((AM_SEEKING_CanSeekAbsolute & seekCaps) ==
AM_SEEKING_CanSeekAbsolute);
}
inline float
UnsignedByteToAudioSample(uint8_t aValue)
{

View File

@ -56,12 +56,8 @@ public:
protected:
void NotifyDataArrivedInternal() override;
public:
bool IsMediaSeekable() override;
private:
// Notifies the filter graph that playback is complete. aStatus is
// the code to send to the filter graph. Always returns false, so
// that we can just "return Finish()" from DecodeAudioData().

View File

@ -185,8 +185,8 @@ private:
struct InitData {
uint32_t mPromiseId;
nsAutoString mOrigin;
nsAutoString mTopLevelOrigin;
nsString mOrigin;
nsString mTopLevelOrigin;
nsString mGMPName;
bool mInPrivateBrowsing;
};
@ -208,7 +208,7 @@ private:
dom::SessionType mSessionType;
uint32_t mCreateSessionToken;
PromiseId mPromiseId;
nsAutoCString mInitDataType;
nsCString mInitDataType;
nsTArray<uint8_t> mInitData;
};
// GMP thread only.
@ -216,7 +216,7 @@ private:
struct SessionOpData {
PromiseId mPromiseId;
nsAutoCString mSessionId;
nsCString mSessionId;
};
// GMP thread only.
void gmp_LoadSession(nsAutoPtr<SessionOpData> aData);
@ -230,7 +230,7 @@ private:
struct UpdateSessionData {
PromiseId mPromiseId;
nsAutoCString mSessionId;
nsCString mSessionId;
nsTArray<uint8_t> mResponse;
};
// GMP thread only.
@ -326,7 +326,7 @@ private:
// destructor. only use on main thread, and always nullcheck before using!
MainThreadOnlyRawPtr<dom::MediaKeys> mKeys;
const nsAutoString mKeySystem;
const nsString mKeySystem;
// Gecko Media Plugin thread. All interactions with the out-of-process
// EME plugin must come from this thread.

View File

@ -1,5 +1,5 @@
Name: fake
Description: Fake GMP Plugin
Description: Fake GMP Plugin, which deliberately uses GMP_API_DECRYPTOR_BACKWARDS_COMPAT for its decryptor.
Version: 1.0
APIs: decode-video[h264:broken], eme-decrypt-v7[fake]
Libraries: dxva2.dll

View File

@ -72,7 +72,7 @@ extern "C" {
// happens when decoder init fails.
return GMPGenericErr;
#if defined(GMP_FAKE_SUPPORT_DECRYPT)
} else if (!strcmp (aApiName, GMP_API_DECRYPTOR)) {
} else if (!strcmp (aApiName, GMP_API_DECRYPTOR_BACKWARDS_COMPAT)) {
*aPluginApi = new FakeDecryptor(static_cast<GMPDecryptorHost*> (aHostAPI));
return GMPNoErr;
} else if (!strcmp (aApiName, GMP_API_ASYNC_SHUTDOWN)) {

View File

@ -119,7 +119,16 @@ GMPContentChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
void* session = nullptr;
GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &session);
if (err != GMPNoErr || !session) {
return false;
// We Adapt the previous GMPDecryptor version to the current, so that
// Gecko thinks it's only talking to the current version. Helpfully,
// v7 is ABI compatible with v8, it only has different enumerations.
// If the GMP uses a v8-only enum value in an IPDL message, the IPC
// layer will terminate, so we rev'd the API version to signal to the
// GMP that it's safe to use the new enum values.
err = mGMPChild->GetAPI(GMP_API_DECRYPTOR_BACKWARDS_COMPAT, host, &session);
if (err != GMPNoErr || !session) {
return false;
}
}
child->Init(static_cast<GMPDecryptor*>(session));

View File

@ -79,7 +79,7 @@ GMPDecryptorChild::SetSessionId(uint32_t aCreateSessionToken,
uint32_t aSessionIdLength)
{
CALL_ON_GMP_THREAD(SendSetSessionId,
aCreateSessionToken, nsAutoCString(aSessionId, aSessionIdLength));
aCreateSessionToken, nsCString(aSessionId, aSessionIdLength));
}
void
@ -102,7 +102,7 @@ GMPDecryptorChild::RejectPromise(uint32_t aPromiseId,
uint32_t aMessageLength)
{
CALL_ON_GMP_THREAD(SendRejectPromise,
aPromiseId, aException, nsAutoCString(aMessage, aMessageLength));
aPromiseId, aException, nsCString(aMessage, aMessageLength));
}
void
@ -115,7 +115,7 @@ GMPDecryptorChild::SessionMessage(const char* aSessionId,
nsTArray<uint8_t> msg;
msg.AppendElements(aMessage, aMessageLength);
CALL_ON_GMP_THREAD(SendSessionMessage,
nsAutoCString(aSessionId, aSessionIdLength),
nsCString(aSessionId, aSessionIdLength),
aMessageType, Move(msg));
}
@ -125,7 +125,7 @@ GMPDecryptorChild::ExpirationChange(const char* aSessionId,
GMPTimestamp aExpiryTime)
{
CALL_ON_GMP_THREAD(SendExpirationChange,
nsAutoCString(aSessionId, aSessionIdLength), aExpiryTime);
nsCString(aSessionId, aSessionIdLength), aExpiryTime);
}
void
@ -133,7 +133,7 @@ GMPDecryptorChild::SessionClosed(const char* aSessionId,
uint32_t aSessionIdLength)
{
CALL_ON_GMP_THREAD(SendSessionClosed,
nsAutoCString(aSessionId, aSessionIdLength));
nsCString(aSessionId, aSessionIdLength));
}
void
@ -145,9 +145,9 @@ GMPDecryptorChild::SessionError(const char* aSessionId,
uint32_t aMessageLength)
{
CALL_ON_GMP_THREAD(SendSessionError,
nsAutoCString(aSessionId, aSessionIdLength),
nsCString(aSessionId, aSessionIdLength),
aException, aSystemCode,
nsAutoCString(aMessage, aMessageLength));
nsCString(aMessage, aMessageLength));
}
void
@ -160,7 +160,7 @@ GMPDecryptorChild::KeyStatusChanged(const char* aSessionId,
nsAutoTArray<uint8_t, 16> kid;
kid.AppendElements(aKeyId, aKeyIdLength);
CALL_ON_GMP_THREAD(SendKeyStatusChanged,
nsAutoCString(aSessionId, aSessionIdLength), kid,
nsCString(aSessionId, aSessionIdLength), kid,
aStatus);
}

View File

@ -868,6 +868,12 @@ GMPParent::ReadGMPMetaData()
}
}
// We support the current GMPDecryptor version, and the previous.
// We Adapt the previous to the current in the GMPContentChild.
if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR_BACKWARDS_COMPAT)) {
cap->mAPIName.AssignLiteral(GMP_API_DECRYPTOR);
}
if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR)) {
mCanDecrypt = true;

View File

@ -209,7 +209,7 @@ private:
nsCOMPtr<nsITimer> mAsyncShutdownTimeout; // GMP Thread only.
// NodeId the plugin is assigned to, or empty if the the plugin is not
// assigned to a NodeId.
nsAutoCString mNodeId;
nsCString mNodeId;
// This is used for GMP content in the parent, there may be more of these in
// the content processes.
RefPtr<GMPContentParent> mGMPContentParent;

View File

@ -156,7 +156,7 @@ private:
void Update(const nsCString& aPlugin, const nsCString& aInstance,
char aId, const nsCString& aState);
private:
struct State { nsAutoCString mStateSequence; nsCString mLastStateDescription; };
struct State { nsCString mStateSequence; nsCString mLastStateDescription; };
typedef nsClassHashtable<nsCStringHashKey, State> StatesByInstance;
typedef nsClassHashtable<nsCStringHashKey, StatesByInstance> StateInstancesByPlugin;
StateInstancesByPlugin mStates;

View File

@ -482,7 +482,7 @@ private:
// Hash record name to record data.
nsClassHashtable<nsCStringHashKey, Record> mRecords;
const nsAutoCString mNodeId;
const nsCString mNodeId;
const nsString mGMPName;
};

View File

@ -244,7 +244,9 @@ enum GMPSessionType {
kGMPSessionInvalid = 2 // Must always be last.
};
#define GMP_API_DECRYPTOR "eme-decrypt-v7"
// Gecko supports the current GMPDecryptor version, and the previous.
#define GMP_API_DECRYPTOR "eme-decrypt-v8"
#define GMP_API_DECRYPTOR_BACKWARDS_COMPAT "eme-decrypt-v7"
// API exposed by plugin library to manage decryption sessions.
// When the Host requests this by calling GMPGetAPIFunc().

View File

@ -468,12 +468,15 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
/* report the duration */
gint64 duration;
bool isMediaSeekable = false;
if (isMP3 && mMP3FrameParser.IsMP3()) {
// The MP3FrameParser has reported a duration; use that over the gstreamer
// reported duration for inter-platform consistency.
mUseParserDuration = true;
mLastParserDuration = mMP3FrameParser.GetDuration();
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(mLastParserDuration));
isMediaSeekable = true;
} else {
LOG(LogLevel::Debug, "querying duration");
// Otherwise use the gstreamer duration.
@ -488,9 +491,12 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
LOG(LogLevel::Debug, "have duration %" GST_TIME_FORMAT, GST_TIME_ARGS(duration));
duration = GST_TIME_AS_USECONDS (duration);
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration));
isMediaSeekable = true;
}
}
mInfo.mMediaSeekable = isMediaSeekable;
int n_video = 0, n_audio = 0;
g_object_get(mPlayBin, "n-video", &n_video, "n-audio", &n_audio, nullptr);
@ -518,28 +524,6 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
bool
GStreamerReader::IsMediaSeekable()
{
if (mUseParserDuration) {
return true;
}
gint64 duration;
#if GST_VERSION_MAJOR >= 1
if (gst_element_query_duration(GST_ELEMENT(mPlayBin), GST_FORMAT_TIME,
&duration)) {
#else
GstFormat format = GST_FORMAT_TIME;
if (gst_element_query_duration(GST_ELEMENT(mPlayBin), &format, &duration) &&
format == GST_FORMAT_TIME) {
#endif
return true;
}
return false;
}
nsresult GStreamerReader::CheckSupportedFormats()
{
bool done = false;

View File

@ -54,11 +54,10 @@ public:
protected:
virtual void NotifyDataArrivedInternal() override;
public:
layers::ImageContainer* GetImageContainer() { return mDecoder->GetImageContainer(); }
virtual bool IsMediaSeekable() override;
private:
bool HasAudio() { return mInfo.HasAudio(); }
bool HasVideo() { return mInfo.HasVideo(); }

View File

@ -486,20 +486,17 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
} else {
return NS_ERROR_FAILURE;
}
{
ReentrantMonitorAutoEnter mon(mMonitor);
mInfo.mMediaSeekable = !mIsChained;
}
*aInfo = mInfo;
return NS_OK;
}
bool
OggReader::IsMediaSeekable()
{
if (mIsChained) {
return false;
}
return true;
}
nsresult OggReader::DecodeVorbis(ogg_packet* aPacket) {
NS_ASSERTION(aPacket->granulepos != -1, "Must know vorbis granulepos!");
@ -712,7 +709,7 @@ void OggReader::SetChained(bool aIsChained) {
ReentrantMonitorAutoEnter mon(mMonitor);
mIsChained = aIsChained;
}
mDecoder->DispatchSetMediaSeekable(false);
mOnMediaNotSeekable.Notify();
}
bool OggReader::ReadOggChain()

View File

@ -66,8 +66,6 @@ public:
Seek(int64_t aTime, int64_t aEndTime) override;
virtual media::TimeIntervals GetBuffered() override;
virtual bool IsMediaSeekable() override;
private:
bool HasAudio() {
return (mVorbisState != 0 && mVorbisState->mActive) ||

View File

@ -310,6 +310,8 @@ void MediaOmxReader::HandleResourceAllocated()
mInfo.mAudio.mRate = sampleRate;
}
mInfo.mMediaSeekable = mExtractor->flags() & MediaExtractor::CAN_SEEK;
RefPtr<MetadataHolder> metadata = new MetadataHolder();
metadata->mInfo = mInfo;
metadata->mTags = nullptr;
@ -321,13 +323,6 @@ void MediaOmxReader::HandleResourceAllocated()
mMetadataPromise.Resolve(metadata, __func__);
}
bool
MediaOmxReader::IsMediaSeekable()
{
// Check the MediaExtract flag if the source is seekable.
return (mExtractor->flags() & MediaExtractor::CAN_SEEK);
}
bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold)
{

View File

@ -92,8 +92,6 @@ public:
virtual RefPtr<SeekPromise>
Seek(int64_t aTime, int64_t aEndTime) override;
virtual bool IsMediaSeekable() override;
virtual void SetIdle() override;
virtual RefPtr<ShutdownPromise> Shutdown() override;

View File

@ -101,13 +101,6 @@ nsresult RawReader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
bool
RawReader::IsMediaSeekable()
{
// not used
return true;
}
bool RawReader::DecodeAudioData()
{
MOZ_ASSERT(OnTaskQueue());

View File

@ -33,8 +33,6 @@ public:
virtual media::TimeIntervals GetBuffered() override;
virtual bool IsMediaSeekable() override;
private:
bool ReadFromResource(uint8_t *aBuf, uint32_t aLength);

View File

@ -49,6 +49,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' # no windowshare on b2g/andr
skip-if = buildapp == 'b2g' || toolkit == 'android' # no windowshare on b2g/android # Bug 1141029 Mulet parity with B2G Desktop for TC
[test_getUserMedia_basicVideoAudio.html]
skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure, turned an intermittent (bug 962579) into a permanant orange
[test_getUserMedia_bug1223696.html]
[test_getUserMedia_constraints.html]
[test_getUserMedia_callbacks.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # Bug 1063290, intermittent timeout # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.

View File

@ -0,0 +1,48 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
"use strict";
createHTML({
title: "Testing that removeTrack+addTrack of video tracks still render the correct track in a media element",
bug: "1223696",
visible: true
});
runTest(() => Promise.resolve()
.then(() => getUserMedia({audio:true, video: true})).then(stream => {
info("Test addTrack()ing a video track to an audio-only gUM stream");
var video = createMediaElement("video", "test_video_track");
video.srcObject = stream;
video.play();
var h = new CaptureStreamTestHelper2D();
stream.removeTrack(stream.getVideoTracks()[0]);
video.onloadeddata = () => {
info("loadeddata");
var canvas = document.createElement("canvas");
canvas.getContext("2d");
var canvasStream = canvas.captureStream();
setInterval(() => h.drawColor(canvas, h.grey), 1000);
stream.addTrack(canvasStream.getVideoTracks()[0]);
checkMediaStreamContains(stream, [stream.getAudioTracks()[0],
canvasStream.getVideoTracks()[0]]);
};
return listenUntil(video, "loadeddata", () => true)
.then(() => h.waitForPixelColor(video, h.grey, 5,
"The canvas track should be rendered by the media element"));
}));
</script>
</pre>
</body>
</html>

View File

@ -145,13 +145,6 @@ nsresult WaveReader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
bool
WaveReader::IsMediaSeekable()
{
// not used
return true;
}
template <typename T> T UnsignedByteToAudioSample(uint8_t aValue);
template <typename T> T SignedShortToAudioSample(int16_t aValue);

View File

@ -33,8 +33,6 @@ public:
virtual media::TimeIntervals GetBuffered() override;
virtual bool IsMediaSeekable() override;
private:
bool ReadAll(char* aBuf, int64_t aSize, int64_t* aBytesRead = nullptr);
bool LoadRIFFChunk();

View File

@ -46,12 +46,6 @@ BufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
// ignore
}
void
BufferDecoder::SetMediaSeekable(bool aMediaSeekable)
{
// ignore
}
VideoFrameContainer*
BufferDecoder::GetVideoFrameContainer()
{

View File

@ -36,8 +36,6 @@ public:
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
uint32_t aDropped) final override;
virtual void SetMediaSeekable(bool aMediaSeekable) final override;
virtual VideoFrameContainer* GetVideoFrameContainer() final override;
virtual layers::ImageContainer* GetImageContainer() final override;

View File

@ -611,7 +611,7 @@ size_t
WebAudioDecodeJob::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t amount = 0;
amount += mContentType.SizeOfExcludingThisMustBeUnshared(aMallocSizeOf);
amount += mContentType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
if (mSuccessCallback) {
amount += mSuccessCallback->SizeOfIncludingThis(aMallocSizeOf);
}

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