mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
5adcabb340
@ -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) {
|
||||
|
@ -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() ||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -71,7 +71,7 @@
|
||||
turnCaretBrowsing(true);
|
||||
|
||||
// test caret offsets
|
||||
testCaretOffset(document, 15);
|
||||
testCaretOffset(document, 16);
|
||||
testCaretOffset("textbox", -1);
|
||||
testCaretOffset("textarea", -1);
|
||||
testCaretOffset("p", -1);
|
||||
|
@ -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 "
|
||||
}
|
||||
]
|
||||
};
|
||||
|
12
browser/components/contextualidentity/moz.build
Normal file
12
browser/components/contextualidentity/moz.build
Normal 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')
|
@ -0,0 +1,7 @@
|
||||
[DEFAULT]
|
||||
skip-if = buildapp == "mulet"
|
||||
support-files =
|
||||
file_reflect_cookie_into_title.html
|
||||
|
||||
[browser_usercontext.js]
|
||||
skip-if = e10s
|
@ -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);
|
||||
}
|
||||
});
|
@ -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>
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
DIRS += [
|
||||
'about',
|
||||
'contextualidentity',
|
||||
'customizableui',
|
||||
'dirprovider',
|
||||
'downloads',
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -65,6 +65,6 @@ private:
|
||||
bool mFixupCreatedAlternateURI;
|
||||
nsString mKeywordProviderName;
|
||||
nsString mKeywordAsSent;
|
||||
nsAutoCString mOriginalInput;
|
||||
nsCString mOriginalInput;
|
||||
};
|
||||
#endif
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -34,7 +34,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoString mRestyleHint;
|
||||
nsString mRestyleHint;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -111,6 +111,11 @@ public:
|
||||
|
||||
bool ElementHasHref() const;
|
||||
|
||||
void TryDNSPrefetch();
|
||||
|
||||
void CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag,
|
||||
nsWrapperCache::FlagsType aRequestedFlag);
|
||||
|
||||
protected:
|
||||
virtual ~Link();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -2405,3 +2405,6 @@ GK_ATOM(onboundary, "onboundary")
|
||||
#endif
|
||||
|
||||
GK_ATOM(vr_state, "vr-state")
|
||||
|
||||
// Contextual Identity / Containers
|
||||
GK_ATOM(usercontextid, "usercontextid")
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 ||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
21
dom/html/crashtests/1228876.html
Normal file
21
dom/html/crashtests/1228876.html
Normal 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>
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -135,7 +135,7 @@ private:
|
||||
uint32_t mLength;
|
||||
uint32_t mOffset;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
const nsAutoCString mContentType;
|
||||
const nsCString mContentType;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -232,6 +232,10 @@ public:
|
||||
return mMetadataManager.TimedMetadataEvent();
|
||||
}
|
||||
|
||||
MediaEventSource<void>& OnMediaNotSeekable() {
|
||||
return mReader->OnMediaNotSeekable();
|
||||
}
|
||||
|
||||
MediaEventSourceExc<nsAutoPtr<MediaInfo>,
|
||||
nsAutoPtr<MetadataTags>,
|
||||
MediaDecoderEventVisibility>&
|
||||
|
@ -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__);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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().
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -482,7 +482,7 @@ private:
|
||||
|
||||
// Hash record name to record data.
|
||||
nsClassHashtable<nsCStringHashKey, Record> mRecords;
|
||||
const nsAutoCString mNodeId;
|
||||
const nsCString mNodeId;
|
||||
const nsString mGMPName;
|
||||
};
|
||||
|
||||
|
@ -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().
|
||||
|
@ -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;
|
||||
|
@ -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(); }
|
||||
|
@ -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()
|
||||
|
@ -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) ||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -33,8 +33,6 @@ public:
|
||||
|
||||
virtual media::TimeIntervals GetBuffered() override;
|
||||
|
||||
virtual bool IsMediaSeekable() override;
|
||||
|
||||
private:
|
||||
bool ReadFromResource(uint8_t *aBuf, uint32_t aLength);
|
||||
|
||||
|
@ -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.
|
||||
|
48
dom/media/tests/mochitest/test_getUserMedia_bug1223696.html
Normal file
48
dom/media/tests/mochitest/test_getUserMedia_bug1223696.html
Normal 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>
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -46,12 +46,6 @@ BufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
|
||||
// ignore
|
||||
}
|
||||
|
||||
void
|
||||
BufferDecoder::SetMediaSeekable(bool aMediaSeekable)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
VideoFrameContainer*
|
||||
BufferDecoder::GetVideoFrameContainer()
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user