merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-12-02 15:20:57 +01:00
Родитель 541bf4c5e1 cc17273b85
Коммит 5adcabb340
503 изменённых файлов: 26199 добавлений и 8428 удалений

Просмотреть файл

@ -230,7 +230,9 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
nsIContent* containerElm = containerNode->IsElement() ? nsIContent* containerElm = containerNode->IsElement() ?
containerNode->AsElement() : nullptr; 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. // Remove text accessible if rendered text is empty.
if (textAcc) { if (textAcc) {

Просмотреть файл

@ -1091,7 +1091,9 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
// Create accessible for visible text frames. // Create accessible for visible text frames.
if (content->IsNodeOfType(nsINode::eTEXT)) { 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 // Ignore not rendered text nodes and whitespace text nodes between table
// cells. // cells.
if (text.mString.IsEmpty() || if (text.mString.IsEmpty() ||

Просмотреть файл

@ -139,7 +139,9 @@ nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent,
if (aContent->TextLength() > 0) { if (aContent->TextLength() > 0) {
nsIFrame *frame = aContent->GetPrimaryFrame(); nsIFrame *frame = aContent->GetPrimaryFrame();
if (frame) { 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); aString->Append(text.mString);
} else { } else {
// If aContent is an object that is display: none, we have no a frame. // 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 && if (frame->GetType() == nsGkAtoms::textFrame &&
!(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
frame->GetRect().IsEmpty()) { 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()) { if (text.mString.IsEmpty()) {
return states::INVISIBLE; return states::INVISIBLE;
} }

Просмотреть файл

@ -1985,7 +1985,8 @@ HyperTextAccessible::ContentToRenderedOffset(nsIFrame* aFrame, int32_t aContentO
"Call on primary frame only"); "Call on primary frame only");
nsIFrame::RenderedText text = aFrame->GetRenderedText(aContentOffset, 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; *aRenderedOffset = text.mOffsetWithinNodeRenderedText;
return NS_OK; return NS_OK;
@ -2009,7 +2010,8 @@ HyperTextAccessible::RenderedToContentOffset(nsIFrame* aFrame, uint32_t aRendere
"Call on primary frame only"); "Call on primary frame only");
nsIFrame::RenderedText text = aFrame->GetRenderedText(aRenderedOffset, 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; *aContentOffset = text.mOffsetWithinNodeText;
return NS_OK; return NS_OK;

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -14,9 +14,9 @@
function doTest() function doTest()
{ {
testTextAtOffset("line_test_1", BOUNDARY_LINE_START, testTextAtOffset("line_test_1", BOUNDARY_LINE_START,
[[0, 5, "Line 1", 0, 6], [[0, 6, "Line 1 ", 0, 7],
[6, 6, "", 6, 6], [7, 7, "", 7, 7],
[7, 13, "Line 3", 7, 13]]); [8, 15, "Line 3 ", 8, 15]]);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__ // __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 ] ]); [ [ 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, testTextAtOffset([ "ht_4" ], BOUNDARY_LINE_START,
[ [ 0, 11, "Hello world", 0, 11 ] ]); [ [ 0, 12, "Hello world ", 0, 12 ] ]);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// list items // list items

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -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 += [ DIRS += [
'about', 'about',
'contextualidentity',
'customizableui', 'customizableui',
'dirprovider', 'dirprovider',
'downloads', 'downloads',

Просмотреть файл

@ -228,6 +228,9 @@ l10n-check::
$(RM) -rf x-test $(RM) -rf x-test
$(NSINSTALL) -D x-test/toolkit $(NSINSTALL) -D x-test/toolkit
echo '#define MOZ_LANG_TITLE Just testing' > x-test/toolkit/defines.inc 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) $(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 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 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]) AC_MSG_ERROR([Your toolchain does not support C++0x/C++11 mode properly. Please upgrade your toolchain])
fi 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 fi
if test -n "$CROSS_COMPILE"; then if test -n "$CROSS_COMPILE"; then
dnl When cross compile, we have no variable telling us what the host compiler is. Figure it out. dnl When cross compile, we have no variable telling us what the host compiler is. Figure it out.
cat > conftest.C <<EOF cat > conftest.C <<EOF

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -14,6 +14,7 @@
#include "nsEscape.h" #include "nsEscape.h"
#include "nsGkAtoms.h" #include "nsGkAtoms.h"
#include "nsHTMLDNSPrefetch.h"
#include "nsString.h" #include "nsString.h"
#include "mozAutoDocUpdate.h" #include "mozAutoDocUpdate.h"
@ -46,6 +47,31 @@ Link::ElementHasHref() const
mElement->HasAttr(kNameSpaceID_XLink, nsGkAtoms::href))); 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 void
Link::SetLinkState(nsLinkState aState) Link::SetLinkState(nsLinkState aState)
{ {

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -14,7 +14,6 @@
#include "jsapi.h" #include "jsapi.h"
#include "jsfriendapi.h" #include "jsfriendapi.h"
#include "xpcpublic.h" #include "xpcpublic.h"
#include "nsIUnicodeDecoder.h"
#include "nsIContent.h" #include "nsIContent.h"
#include "nsJSUtils.h" #include "nsJSUtils.h"
#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ScriptSettings.h"
@ -54,7 +53,6 @@
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/unused.h" #include "mozilla/unused.h"
#include "mozilla/dom/SRICheck.h"
#include "nsIScriptError.h" #include "nsIScriptError.h"
using namespace mozilla; using namespace mozilla;
@ -157,10 +155,10 @@ nsScriptLoader::~nsScriptLoader()
// subtree in the meantime and therefore aren't actually going away. // subtree in the meantime and therefore aren't actually going away.
for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) { for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) {
mPendingChildLoaders[j]->RemoveExecuteBlocker(); 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 // 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. // This means that it has both a for-attribute and a event-attribute.
@ -269,37 +267,6 @@ nsScriptLoader::ShouldLoadScript(nsIDocument* aDocument,
return NS_OK; 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 nsresult
nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
bool aScriptFromHead) bool aScriptFromHead)
@ -383,10 +350,16 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
timedChannel->SetInitiatorType(NS_LITERAL_STRING("script")); 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; RefPtr<nsScriptLoadHandler> handler =
rv = NS_NewStreamLoader(getter_AddRefs(loader), mediator); new nsScriptLoadHandler(this, aRequest, sriDataVerifier.forget());
nsCOMPtr<nsIIncrementalStreamLoader> loader;
rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), handler);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return channel->AsyncOpen2(loader); return channel->AsyncOpen2(loader);
@ -1438,23 +1411,36 @@ nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
return rv; return rv;
} }
NS_IMETHODIMP nsresult
nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext, nsISupports* aContext,
nsresult aStatus, nsresult aChannelStatus,
uint32_t aStringLen, nsresult aSRIStatus,
const uint8_t* aString) mozilla::Vector<char16_t> &aString,
mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier)
{ {
nsScriptLoadRequest* request = static_cast<nsScriptLoadRequest*>(aContext); nsScriptLoadRequest* request = static_cast<nsScriptLoadRequest*>(aContext);
NS_ASSERTION(request, "null request in stream complete handler"); NS_ASSERTION(request, "null request in stream complete handler");
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsresult rv = NS_ERROR_SRI_CORRUPT; nsresult rv = NS_OK;
if (request->mIntegrity.IsEmpty() || if (!request->mIntegrity.IsEmpty() &&
NS_SUCCEEDED(SRICheck::VerifyIntegrity(request->mIntegrity, aLoader, NS_SUCCEEDED((rv = aSRIStatus))) {
request->mCORSMode, aStringLen, MOZ_ASSERT(aSRIDataVerifier);
aString, mDocument))) {
rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen, aString); 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)) { if (NS_FAILED(rv)) {
@ -1497,16 +1483,12 @@ nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
} else { } else {
mPreloads.RemoveElement(request, PreloadRequestComparator()); 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 // Process our request and/or any pending ones
ProcessPendingRequests(); ProcessPendingRequests();
return rv; return NS_OK;
} }
void void
@ -1535,10 +1517,9 @@ nsScriptLoader::NumberOfProcessors()
nsresult nsresult
nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
nsIStreamLoader* aLoader, nsIIncrementalStreamLoader* aLoader,
nsresult aStatus, nsresult aStatus,
uint32_t aStringLen, mozilla::Vector<char16_t> &aString)
const uint8_t* aString)
{ {
if (NS_FAILED(aStatus)) { if (NS_FAILED(aStatus)) {
return aStatus; return aStatus;
@ -1586,21 +1567,9 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
if (aStringLen) { if (!aString.empty()) {
// Check the charset attribute to determine script charset. aRequest->mScriptTextLength = aString.length();
nsAutoString hintCharset; aRequest->mScriptTextBuf = aString.extractRawBuffer();
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);
} }
// This assertion could fire errorously if we ran out of memory when // This assertion could fire errorously if we ran out of memory when
@ -1739,3 +1708,192 @@ nsScriptLoader::MaybeRemovedDeferRequests()
} }
return false; 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__ #define __nsScriptLoader_h__
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIUnicodeDecoder.h"
#include "nsIScriptElement.h" #include "nsIScriptElement.h"
#include "nsCOMArray.h" #include "nsCOMArray.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include "nsIDocument.h" #include "nsIDocument.h"
#include "nsIStreamLoader.h" #include "nsIIncrementalStreamLoader.h"
#include "mozilla/CORSMode.h" #include "mozilla/CORSMode.h"
#include "mozilla/dom/SRIMetadata.h" #include "mozilla/dom/SRIMetadata.h"
#include "mozilla/dom/SRICheck.h"
#include "mozilla/LinkedList.h" #include "mozilla/LinkedList.h"
#include "mozilla/net/ReferrerPolicy.h" #include "mozilla/net/ReferrerPolicy.h"
#include "mozilla/Vector.h"
class nsScriptLoadRequestList; class nsScriptLoadRequestList;
class nsIURI; class nsIURI;
@ -195,7 +198,7 @@ public:
// Script loader implementation // Script loader implementation
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
class nsScriptLoader final : public nsIStreamLoaderObserver class nsScriptLoader final : public nsISupports
{ {
class MOZ_STACK_CLASS AutoCurrentScriptUpdater class MOZ_STACK_CLASS AutoCurrentScriptUpdater
{ {
@ -217,13 +220,13 @@ class nsScriptLoader final : public nsIStreamLoaderObserver
}; };
friend class nsScriptRequestProcessor; friend class nsScriptRequestProcessor;
friend class nsScriptLoadHandler;
friend class AutoCurrentScriptUpdater; friend class AutoCurrentScriptUpdater;
public: public:
explicit nsScriptLoader(nsIDocument* aDocument); explicit nsScriptLoader(nsIDocument* aDocument);
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLOADEROBSERVER
/** /**
* The loader maintains a weak reference to the document with * The loader maintains a weak reference to the document with
@ -342,6 +345,18 @@ public:
nsIDocument* aDocument, nsIDocument* aDocument,
char16_t*& aBufOut, size_t& aLengthOut); 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. * Processes any pending requests that are ready for processing.
*/ */
@ -489,10 +504,9 @@ private:
uint32_t NumberOfProcessors(); uint32_t NumberOfProcessors();
nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest, nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
nsIStreamLoader* aLoader, nsIIncrementalStreamLoader* aLoader,
nsresult aStatus, nsresult aStatus,
uint32_t aStringLen, mozilla::Vector<char16_t> &aString);
const uint8_t* aString);
void AddDeferRequest(nsScriptLoadRequest* aRequest); void AddDeferRequest(nsScriptLoadRequest* aRequest);
bool MaybeRemovedDeferRequests(); bool MaybeRemovedDeferRequests();
@ -538,6 +552,53 @@ private:
bool mBlockingDOMContentLoaded; 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 class nsAutoScriptLoaderDisabler
{ {
public: public:

Просмотреть файл

@ -2534,6 +2534,55 @@ CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissio
return true; 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 void
HandlePrerenderingViolation(nsPIDOMWindow* aWindow) HandlePrerenderingViolation(nsPIDOMWindow* aWindow)
{ {

Просмотреть файл

@ -3160,16 +3160,6 @@ AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
JS::Handle<JS::Value> aValue); JS::Handle<JS::Value> aValue);
#endif #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 // 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 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 // that the |this| object is not running in a global that is in prerendering

Просмотреть файл

@ -1906,16 +1906,26 @@ def getAvailableInTestFunc(obj):
class MemberCondition: class MemberCondition:
""" """
An object representing the condition for a member to actually be An object representing the condition for a member to actually be
exposed. Any of pref, func, and available can be None. If not exposed. Any of the arguments can be None. If not
None, they should be strings that have the pref name (for "pref") None, they should have the following types:
or function name (for "func" and "available").
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 pref is None or isinstance(pref, str)
assert func is None or isinstance(func, str) assert func is None or isinstance(func, str)
assert available is None or isinstance(available, str) assert available is None or isinstance(available, str)
assert checkAnyPermissions is None or isinstance(checkAnyPermissions, int) assert checkAnyPermissions is None or isinstance(checkAnyPermissions, int)
assert checkAllPermissions is None or isinstance(checkAllPermissions, int) assert checkAllPermissions is None or isinstance(checkAllPermissions, int)
assert nonExposedGlobals is None or isinstance(nonExposedGlobals, set)
self.pref = pref self.pref = pref
def toFuncPtr(val): def toFuncPtr(val):
@ -1933,11 +1943,20 @@ class MemberCondition:
else: else:
self.checkAllPermissions = "allpermissions_%i" % checkAllPermissions 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): def __eq__(self, other):
return (self.pref == other.pref and self.func == other.func and return (self.pref == other.pref and self.func == other.func and
self.available == other.available and self.available == other.available and
self.checkAnyPermissions == other.checkAnyPermissions and self.checkAnyPermissions == other.checkAnyPermissions and
self.checkAllPermissions == other.checkAllPermissions) self.checkAllPermissions == other.checkAllPermissions and
self.nonExposedGlobals == other.nonExposedGlobals)
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
@ -2000,13 +2019,34 @@ class PropertyDefiner:
@staticmethod @staticmethod
def getControllingCondition(interfaceMember, descriptor): def getControllingCondition(interfaceMember, descriptor):
return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember, # We do a slightly complicated thing for exposure sets to deal nicely
"Pref"), # with the situation of an [Exposed=Window] thing on an interface
PropertyDefiner.getStringAttr(interfaceMember, # exposed in workers that has a worker-specific descriptor. In that
"Func"), # situation, we already skip generation of the member entirely in the
getAvailableInTestFunc(interfaceMember), # worker binding, and shouldn't need to check for the various worker
descriptor.checkAnyPermissionsIndicesForMembers.get(interfaceMember.identifier.name), # scopes in the non-worker binding.
descriptor.checkAllPermissionsIndicesForMembers.get(interfaceMember.identifier.name)) 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, def generatePrefableArray(self, array, name, specFormatter, specTerminator,
specType, getCondition, getDataTuple, doIdArrays): specType, getCondition, getDataTuple, doIdArrays):
@ -2044,7 +2084,7 @@ class PropertyDefiner:
specs = [] specs = []
prefableSpecs = [] prefableSpecs = []
prefableTemplate = ' { true, %s, %s, %s, %s, &%s[%d] }' prefableTemplate = ' { true, %s, %s, %s, %s, %s, &%s[%d] }'
prefCacheTemplate = '&%s[%d].enabled' prefCacheTemplate = '&%s[%d].enabled'
def switchToCondition(props, condition): def switchToCondition(props, condition):
@ -2056,7 +2096,8 @@ class PropertyDefiner:
prefCacheTemplate % (name, len(prefableSpecs)))) prefCacheTemplate % (name, len(prefableSpecs))))
# Set up pointers to the new sets of specs inside prefableSpecs # Set up pointers to the new sets of specs inside prefableSpecs
prefableSpecs.append(prefableTemplate % prefableSpecs.append(prefableTemplate %
(condition.func, (condition.nonExposedGlobals,
condition.func,
condition.available, condition.available,
condition.checkAnyPermissions, condition.checkAnyPermissions,
condition.checkAllPermissions, condition.checkAllPermissions,
@ -2075,7 +2116,7 @@ class PropertyDefiner:
# And the actual spec # And the actual spec
specs.append(specFormatter(getDataTuple(member))) specs.append(specFormatter(getDataTuple(member)))
specs.append(specTerminator) specs.append(specTerminator)
prefableSpecs.append(" { false, nullptr }") prefableSpecs.append(" { false, 0, nullptr, nullptr, nullptr, nullptr, nullptr }")
specType = "const " + specType specType = "const " + specType
arrays = fill( arrays = fill(
@ -2198,7 +2239,7 @@ class MethodDefiner(PropertyDefiner):
"methodInfo": False, "methodInfo": False,
"length": 1, "length": 1,
"flags": "0", "flags": "0",
"condition": MemberCondition(None, condition) "condition": MemberCondition(func=condition)
}) })
continue continue
@ -2252,7 +2293,7 @@ class MethodDefiner(PropertyDefiner):
"selfHostedName": "ArrayValues", "selfHostedName": "ArrayValues",
"length": 0, "length": 0,
"flags": "JSPROP_ENUMERATE", "flags": "JSPROP_ENUMERATE",
"condition": MemberCondition(None, None) "condition": MemberCondition()
}) })
# Output an @@iterator for generated iterator interfaces. This should # Output an @@iterator for generated iterator interfaces. This should
@ -2268,7 +2309,7 @@ class MethodDefiner(PropertyDefiner):
"selfHostedName": "IteratorIdentity", "selfHostedName": "IteratorIdentity",
"length": 0, "length": 0,
"flags": "0", "flags": "0",
"condition": MemberCondition(None, None) "condition": MemberCondition()
}) })
# Generate the maplike/setlike iterator, if one wasn't already # Generate the maplike/setlike iterator, if one wasn't already
@ -2341,7 +2382,7 @@ class MethodDefiner(PropertyDefiner):
"length": 0, "length": 0,
"flags": "JSPROP_ENUMERATE", # readonly/permanent added "flags": "JSPROP_ENUMERATE", # readonly/permanent added
# automatically. # automatically.
"condition": MemberCondition(None, None) "condition": MemberCondition()
}) })
if descriptor.interface.isJSImplemented(): if descriptor.interface.isJSImplemented():
@ -2353,7 +2394,7 @@ class MethodDefiner(PropertyDefiner):
"methodInfo": False, "methodInfo": False,
"length": 2, "length": 2,
"flags": "0", "flags": "0",
"condition": MemberCondition(None, None) "condition": MemberCondition()
}) })
else: else:
for m in clearableCachedAttrs(descriptor): for m in clearableCachedAttrs(descriptor):
@ -2364,7 +2405,7 @@ class MethodDefiner(PropertyDefiner):
"methodInfo": False, "methodInfo": False,
"length": "0", "length": "0",
"flags": "0", "flags": "0",
"condition": MemberCondition(None, None) "condition": MemberCondition()
}) })
self.unforgeable = unforgeable self.unforgeable = unforgeable

Просмотреть файл

@ -39,12 +39,24 @@ typedef bool
JS::Handle<JSObject*> obj, JS::Handle<JSObject*> obj,
JS::AutoIdVector& props); 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 bool
CheckAnyPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); 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 bool
CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]); 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 struct ConstantSpec
{ {
const char* name; const char* name;
@ -53,9 +65,35 @@ struct ConstantSpec
typedef bool (*PropertyEnabled)(JSContext* cx, JSObject* global); 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> template<typename T>
struct Prefable { struct Prefable {
inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const { 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) { if (!enabled) {
return false; return false;
} }
@ -85,6 +123,8 @@ struct Prefable {
// A boolean indicating whether this set of specs is enabled // A boolean indicating whether this set of specs is enabled
bool 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 // 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 // even if "enabled" is set to true. If the pointer is null the value of
// "enabled" is used as-is unless availableFunc overrides. // "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_READ_BUFFER:
case LOCAL_GL_COPY_WRITE_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; break;
default: default:

Просмотреть файл

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

Просмотреть файл

@ -25,13 +25,11 @@ protected:
explicit WebGLVertexArrayGL(WebGLContext* webgl); explicit WebGLVertexArrayGL(WebGLContext* webgl);
~WebGLVertexArrayGL(); ~WebGLVertexArrayGL();
#if defined(XP_LINUX)
// Bug 1140459: Some drivers (including our test slaves!) don't // 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. // So we track the `is a VAO` state ourselves.
bool mIsVAO; bool mIsVAO;
#endif
}; };
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -28,6 +28,7 @@ CaptureStreamTestHelper.prototype = {
blackTransparent: { data: [0, 0, 0, 0], name: "blackTransparent" }, blackTransparent: { data: [0, 0, 0, 0], name: "blackTransparent" },
green: { data: [0, 255, 0, 255], name: "green" }, green: { data: [0, 255, 0, 255], name: "green" },
red: { data: [255, 0, 0, 255], name: "red" }, red: { data: [255, 0, 0, 255], name: "red" },
grey: { data: [128, 128, 128, 255], name: "grey" },
/* Default element size for createAndAppendElement() */ /* Default element size for createAndAppendElement() */
elemWidth: 100, elemWidth: 100,
@ -97,7 +98,12 @@ CaptureStreamTestHelper.prototype = {
const startTime = video.currentTime; const startTime = video.currentTime;
CaptureStreamTestHelper2D.prototype.clear.call(this, this.cout); CaptureStreamTestHelper2D.prototype.clear.call(this, this.cout);
var ontimeupdate = () => { 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 && if (!pixelMatch &&
(!timeout || video.currentTime < startTime + (timeout / 1000.0))) { (!timeout || video.currentTime < startTime + (timeout / 1000.0))) {
// No match yet and, // No match yet and,

Просмотреть файл

@ -8,6 +8,7 @@
#include "mozilla/IMEStateManager.h" #include "mozilla/IMEStateManager.h"
#include "mozilla/TextEvents.h" #include "mozilla/TextEvents.h"
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLUnknownElement.h"
#include "mozilla/dom/Selection.h" #include "mozilla/dom/Selection.h"
#include "nsCaret.h" #include "nsCaret.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
@ -24,6 +25,7 @@
#include "nsIObjectFrame.h" #include "nsIObjectFrame.h"
#include "nsLayoutUtils.h" #include "nsLayoutUtils.h"
#include "nsPresContext.h" #include "nsPresContext.h"
#include "nsQueryObject.h"
#include "nsRange.h" #include "nsRange.h"
#include "nsTextFragment.h" #include "nsTextFragment.h"
#include "nsTextFrame.h" #include "nsTextFrame.h"
@ -40,6 +42,65 @@ using namespace widget;
/* ContentEventHandler */ /* 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) ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext)
: mPresContext(aPresContext) : mPresContext(aPresContext)
, mPresShell(aPresContext->GetPresShell()) , mPresShell(aPresContext->GetPresShell())
@ -327,6 +388,9 @@ ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
{ {
MOZ_ASSERT(aEndOffset >= aStartOffset, MOZ_ASSERT(aEndOffset >= aStartOffset,
"aEndOffset must be equals or larger than aStartOffset"); "aEndOffset must be equals or larger than aStartOffset");
if (NS_WARN_IF(!aContent->IsNodeOfType(nsINode::eTEXT))) {
return 0;
}
if (aStartOffset == aEndOffset) { if (aStartOffset == aEndOffset) {
return 0; return 0;
} }
@ -338,11 +402,25 @@ ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
ContentEventHandler::GetNativeTextLength(nsIContent* aContent, ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
uint32_t aMaxLength) uint32_t aMaxLength)
{ {
if (NS_WARN_IF(!aContent->IsNodeOfType(nsINode::eTEXT))) {
return 0;
}
return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength); return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength);
} }
static inline uint32_t /* static */ uint32_t
GetBRLength(LineBreakType aLineBreakType) 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) #if defined(XP_WIN)
// Length of \r\n // Length of \r\n
@ -357,29 +435,26 @@ ContentEventHandler::GetTextLength(nsIContent* aContent,
LineBreakType aLineBreakType, LineBreakType aLineBreakType,
uint32_t aMaxLength) uint32_t aMaxLength)
{ {
if (aContent->IsNodeOfType(nsINode::eTEXT)) { MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
uint32_t textLengthDifference =
uint32_t textLengthDifference =
#if defined(XP_WIN) #if defined(XP_WIN)
// On Windows, the length of a native newline ("\r\n") is twice the length // 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 // 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. // native offset plus the number of newlines encountered in the string.
(aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ?
CountNewlinesInXPLength(aContent, aMaxLength) : 0; CountNewlinesInXPLength(aContent, aMaxLength) : 0;
#else #else
// On other platforms, the native and XP newlines are the same. // On other platforms, the native and XP newlines are the same.
0; 0;
#endif #endif
const nsTextFragment* text = aContent->GetText(); const nsTextFragment* text = aContent->GetText();
if (!text) { if (!text) {
return 0; return 0;
}
uint32_t length = std::min(text->GetLength(), aMaxLength);
return length + textLengthDifference;
} else if (IsContentBR(aContent)) {
return GetBRLength(aLineBreakType);
} }
return 0; uint32_t length = std::min(text->GetLength(), aMaxLength);
return length + textLengthDifference;
} }
static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset) static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
@ -395,37 +470,112 @@ static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
#endif #endif
} }
static nsresult GenerateFlatTextContent(nsRange* aRange, /* static */ bool
nsAFlatString& aString, ContentEventHandler::ShouldBreakLineBefore(nsIContent* aContent,
LineBreakType aLineBreakType) nsINode* aRootNode)
{ {
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator(); // We don't need to append linebreak at the start of the root element.
iter->Init(aRange); 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"); NS_ASSERTION(aString.IsEmpty(), "aString must be empty string");
if (aRange->Collapsed()) {
return NS_OK;
}
nsINode* startNode = aRange->GetStartParent(); nsINode* startNode = aRange->GetStartParent();
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
nsINode* endNode = aRange->GetEndParent(); 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)) { if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
nsIContent* content = static_cast<nsIContent*>(startNode); nsIContent* content = startNode->AsContent();
AppendSubString(aString, content, aRange->StartOffset(), AppendSubString(aString, content, aRange->StartOffset(),
aRange->EndOffset() - aRange->StartOffset()); aRange->EndOffset() - aRange->StartOffset());
ConvertToNativeNewlines(aString); ConvertToNativeNewlines(aString);
return NS_OK; 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()) { for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode(); nsINode* node = iter->GetCurrentNode();
if (!node) { if (NS_WARN_IF(!node)) {
break; break;
} }
if (!node->IsNodeOfType(nsINode::eCONTENT)) { if (!node->IsContent()) {
continue; continue;
} }
nsIContent* content = static_cast<nsIContent*>(node); nsIContent* content = node->AsContent();
if (content->IsNodeOfType(nsINode::eTEXT)) { if (content->IsNodeOfType(nsINode::eTEXT)) {
if (content == startNode) { if (content == startNode) {
@ -436,7 +586,7 @@ static nsresult GenerateFlatTextContent(nsRange* aRange,
} else { } else {
AppendString(aString, content); AppendString(aString, content);
} }
} else if (IsContentBR(content)) { } else if (ShouldBreakLineBefore(content, mRootContent)) {
aString.Append(char16_t('\n')); aString.Append(char16_t('\n'));
} }
} }
@ -460,6 +610,8 @@ ContentEventHandler::GetTextLengthInRange(nsIContent* aContent,
uint32_t aXPEndOffset, uint32_t aXPEndOffset,
LineBreakType aLineBreakType) LineBreakType aLineBreakType)
{ {
MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
return aLineBreakType == LINE_BREAK_TYPE_NATIVE ? return aLineBreakType == LINE_BREAK_TYPE_NATIVE ?
GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) : GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) :
aXPEndOffset - aXPStartOffset; aXPEndOffset - aXPStartOffset;
@ -473,6 +625,8 @@ ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
int32_t aXPEndOffset, int32_t aXPEndOffset,
LineBreakType aLineBreakType) LineBreakType aLineBreakType)
{ {
MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
nsIFrame* frame = aContent->GetPrimaryFrame(); nsIFrame* frame = aContent->GetPrimaryFrame();
if (!frame) { if (!frame) {
// It is a non-rendered content, create an empty range for it. // 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, ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
FontRangeArray& aFontRanges, FontRangeArray& aFontRanges,
uint32_t& aLength, uint32_t& aLength,
@ -560,16 +714,24 @@ ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
{ {
MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array"); MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array");
if (aRange->Collapsed()) {
return NS_OK;
}
nsINode* startNode = aRange->GetStartParent(); nsINode* startNode = aRange->GetStartParent();
nsINode* endNode = aRange->GetEndParent(); nsINode* endNode = aRange->GetEndParent();
if (NS_WARN_IF(!startNode || !endNode)) { if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
// baseOffset is the flattened offset of each content node. // baseOffset is the flattened offset of each content node.
int32_t baseOffset = 0; int32_t baseOffset = 0;
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator(); nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
for (iter->Init(aRange); !iter->IsDone(); iter->Next()) { nsresult rv = iter->Init(aRange);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode(); nsINode* node = iter->GetCurrentNode();
if (NS_WARN_IF(!node)) { if (NS_WARN_IF(!node)) {
break; break;
@ -587,7 +749,7 @@ ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
startOffset, endOffset, aLineBreakType); startOffset, endOffset, aLineBreakType);
baseOffset += GetTextLengthInRange(content, startOffset, endOffset, baseOffset += GetTextLengthInRange(content, startOffset, endOffset,
aLineBreakType); aLineBreakType);
} else if (IsContentBR(content)) { } else if (ShouldBreakLineBefore(content, mRootContent)) {
if (aFontRanges.IsEmpty()) { if (aFontRanges.IsEmpty()) {
MOZ_ASSERT(baseOffset == 0); MOZ_ASSERT(baseOffset == 0);
FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset); FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
@ -672,105 +834,221 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
*aNewOffset = aOffset; *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(); nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
nsresult rv = iter->Init(mRootContent); 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 offset = 0;
uint32_t endOffset = aOffset + aLength; uint32_t endOffset = aOffset + aLength;
bool startSet = false; bool startSet = false;
for (; !iter->IsDone(); iter->Next()) { for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode(); nsINode* node = iter->GetCurrentNode();
if (!node) { if (NS_WARN_IF(!node)) {
break; 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; 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) { if (!textLength) {
continue; continue;
} }
if (offset <= aOffset && aOffset < offset + textLength) { // When the start offset is in between accumulated offset and the last
uint32_t xpOffset; // offset of the node, the node is the start node of the range.
if (!content->IsNodeOfType(nsINode::eTEXT)) { if (!startSet && aOffset <= offset + textLength) {
xpOffset = 0; nsINode* startNode = nullptr;
} else { int32_t startNodeOffset = -1;
xpOffset = aOffset - offset; 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) { if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
xpOffset = ConvertToXPOffset(content, xpOffset); xpOffset = ConvertToXPOffset(content, xpOffset);
} }
}
if (aExpandToClusterBoundaries) { if (aExpandToClusterBoundaries) {
uint32_t oldXPOffset = xpOffset; uint32_t oldXPOffset = xpOffset;
rv = ExpandToClusterBoundary(content, false, &xpOffset); rv = ExpandToClusterBoundary(content, false, &xpOffset);
NS_ENSURE_SUCCESS(rv, rv); if (NS_WARN_IF(NS_FAILED(rv))) {
if (aNewOffset) { return rv;
// This is correct since a cluster shouldn't include line break. }
*aNewOffset -= (oldXPOffset - xpOffset); 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; startSet = true;
if (aLength == 0) {
// Ensure that the end offset and the start offset are same. if (!aLength) {
rv = aRange->SetEnd(content, int32_t(xpOffset)); rv = aRange->SetEnd(startNode, startNodeOffset);
NS_ENSURE_SUCCESS(rv, rv); if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK; return NS_OK;
} }
} }
// When the end offset is in the content, the node is the end node of the
// range.
if (endOffset <= offset + textLength) { if (endOffset <= offset + textLength) {
nsINode* endNode = content; MOZ_ASSERT(startSet,
uint32_t xpOffset; "The start of the range should've been set already");
if (content->IsNodeOfType(nsINode::eTEXT)) { 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) { if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
xpOffset = ConvertToXPOffset(content, xpOffset); xpOffset = ConvertToXPOffset(content, xpOffset);
} }
if (aExpandToClusterBoundaries) { if (aExpandToClusterBoundaries) {
rv = ExpandToClusterBoundary(content, true, &xpOffset); rv = ExpandToClusterBoundary(content, true, &xpOffset);
NS_ENSURE_SUCCESS(rv, rv); if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} }
} else { NS_ASSERTION(xpOffset <= INT32_MAX,
// Use first position of next node, because the end node is ignored "The end node offset is too large");
// by ContentIterator when the offset is zero. rv = aRange->SetEnd(content, static_cast<int32_t>(xpOffset));
xpOffset = 0; if (NS_WARN_IF(NS_FAILED(rv))) {
iter->Next(); return rv;
if (iter->IsDone()) {
break;
} }
endNode = iter->GetCurrentNode(); return NS_OK;
} }
rv = aRange->SetEnd(endNode, int32_t(xpOffset)); if (endOffset == offset) {
NS_ENSURE_SUCCESS(rv, rv); // 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; return NS_OK;
} }
offset += textLength; offset += textLength;
} }
if (offset < aOffset) {
return NS_ERROR_FAILURE;
}
if (!startSet) { if (!startSet) {
MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT)); MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT));
rv = aRange->SetStart(mRootContent, int32_t(mRootContent->GetChildCount())); if (!offset) {
NS_ENSURE_SUCCESS(rv, rv); // 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) { if (aNewOffset) {
*aNewOffset = offset; *aNewOffset = offset;
} }
} }
rv = aRange->SetEnd(mRootContent, int32_t(mRootContent->GetChildCount())); // Rule #2.5: ]</root>
NS_ASSERTION(NS_SUCCEEDED(rv), "nsRange::SetEnd failed"); rv = aRange->SetEnd(mRootContent,
return rv; static_cast<int32_t>(mRootContent->GetChildCount()));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
} }
/* static */ LineBreakType /* static */ LineBreakType
@ -849,8 +1127,8 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
"The reply string must be empty"); "The reply string must be empty");
LineBreakType lineBreakType = GetLineBreakType(aEvent); LineBreakType lineBreakType = GetLineBreakType(aEvent);
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, rv = GetFlatTextLengthBefore(mFirstSelectedRange,
&aEvent->mReply.mOffset, lineBreakType); &aEvent->mReply.mOffset, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINode> anchorNode, focusNode; nsCOMPtr<nsINode> anchorNode, focusNode;
@ -1121,8 +1399,8 @@ ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect); nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect);
if (caretFrame) { if (caretFrame) {
uint32_t offset; uint32_t offset;
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset, rv = GetFlatTextLengthBefore(mFirstSelectedRange,
lineBreakType); &offset, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
if (offset == aEvent->mInput.mOffset) { if (offset == aEvent->mInput.mOffset) {
rv = ConvertToRootRelativeOffset(caretFrame, caretRect); rv = ConvertToRootRelativeOffset(caretFrame, caretRect);
@ -1301,8 +1579,9 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent)
return NS_OK; return NS_OK;
} }
rv = GetFlatTextOffsetOfRange(mRootContent, tentativeCaretOffsets.content, rv = GetFlatTextLengthInRange(NodePosition(mRootContent, 0),
tentativeCaretOffsets.offset, NodePosition(tentativeCaretOffsets),
mRootContent,
&aEvent->mReply.mTentativeCaretOffset, &aEvent->mReply.mTentativeCaretOffset,
GetLineBreakType(aEvent)); GetLineBreakType(aEvent));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
@ -1325,10 +1604,13 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent)
textframe->GetCharacterOffsetAtFramePoint(ptInTarget); textframe->GetCharacterOffsetAtFramePoint(ptInTarget);
NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE); NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE);
uint32_t offset; uint32_t offset;
rv = GetFlatTextOffsetOfRange(mRootContent, contentOffsets.content, rv = GetFlatTextLengthInRange(NodePosition(mRootContent, 0),
contentOffsets.offset, &offset, NodePosition(contentOffsets),
mRootContent, &offset,
GetLineBreakType(aEvent)); GetLineBreakType(aEvent));
NS_ENSURE_SUCCESS(rv, rv); if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
WidgetQueryContentEvent textRect(true, eQueryTextRect, aEvent->widget); WidgetQueryContentEvent textRect(true, eQueryTextRect, aEvent->widget);
textRect.InitForQueryTextRect(offset, 1, aEvent->mUseNativeLineBreak); textRect.InitForQueryTextRect(offset, 1, aEvent->mUseNativeLineBreak);
@ -1390,82 +1672,158 @@ ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent)
} }
/* static */ nsresult /* static */ nsresult
ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, ContentEventHandler::GetFlatTextLengthInRange(
nsINode* aNode, const NodePosition& aStartPosition,
int32_t aNodeOffset, const NodePosition& aEndPosition,
uint32_t* aOffset, nsIContent* aRootContent,
LineBreakType aLineBreakType) uint32_t* aLength,
LineBreakType aLineBreakType,
bool aIsRemovingNode /* = false */)
{ {
NS_ENSURE_STATE(aRootContent); if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsValid()) ||
NS_ASSERTION(aOffset, "param is invalid"); NS_WARN_IF(!aEndPosition.IsValid()) || NS_WARN_IF(!aLength)) {
return NS_ERROR_INVALID_ARG;
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);
} }
nsCOMPtr<nsINode> startNode = do_QueryInterface(startDOMNode); if (aStartPosition == aEndPosition) {
nsINode* endNode = aNode; *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()) { for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode(); nsINode* node = iter->GetCurrentNode();
if (!node) { if (NS_WARN_IF(!node)) {
break; break;
} }
if (!node->IsNodeOfType(nsINode::eCONTENT)) { if (!node->IsContent()) {
continue; continue;
} }
nsIContent* content = static_cast<nsIContent*>(node); nsIContent* content = node->AsContent();
if (node->IsNodeOfType(nsINode::eTEXT)) { if (node->IsNodeOfType(nsINode::eTEXT)) {
// Note: our range always starts from offset 0 // Note: our range always starts from offset 0
if (node == endNode) { if (node == aEndPosition.mNode) {
*aOffset += GetTextLength(content, aLineBreakType, aNodeOffset); *aLength += GetTextLength(content, aLineBreakType,
aEndPosition.mOffset);
} else { } else {
*aOffset += GetTextLength(content, aLineBreakType); *aLength += GetTextLength(content, aLineBreakType);
} }
} else if (IsContentBR(content)) { } else if (ShouldBreakLineBefore(content, aRootContent)) {
#if defined(XP_WIN) // If the start position is start of this node but doesn't include the
// On Windows, the length of the newline is 2. // open tag, don't append the line break length.
*aOffset += (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1; if (node == aStartPosition.mNode && !aStartPosition.IsBeforeOpenTag()) {
#else continue;
// On other platforms, the length of the newline is 1. }
*aOffset += 1; *aLength += GetBRLength(aLineBreakType);
#endif
} }
} }
return NS_OK; return NS_OK;
} }
/* static */ nsresult nsresult
ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, ContentEventHandler::GetFlatTextLengthBefore(nsRange* aRange,
nsRange* aRange, uint32_t* aOffset,
uint32_t* aOffset, LineBreakType aLineBreakType)
LineBreakType aLineBreakType)
{ {
nsINode* startNode = aRange->GetStartParent(); MOZ_ASSERT(aRange);
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); return GetFlatTextLengthInRange(
int32_t startOffset = aRange->StartOffset(); NodePosition(mRootContent, 0),
return GetFlatTextOffsetOfRange(aRootContent, startNode, startOffset, NodePositionBefore(aRange->GetStartParent(), aRange->StartOffset()),
aOffset, aLineBreakType); mRootContent, aOffset, aLineBreakType);
} }
nsresult nsresult
@ -1571,33 +1929,36 @@ static void AdjustRangeForSelection(nsIContent* aRoot,
{ {
nsINode* node = *aNode; nsINode* node = *aNode;
int32_t nodeOffset = *aNodeOffset; int32_t nodeOffset = *aNodeOffset;
if (aRoot != node && node->GetParent()) { if (aRoot == node || NS_WARN_IF(!node->GetParent()) ||
if (node->IsNodeOfType(nsINode::eTEXT)) { !node->IsNodeOfType(nsINode::eTEXT)) {
// When the offset is at the end of the text node, set it to after the return;
// 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);
}
} }
nsIContent* brContent = node->GetChildAt(nodeOffset - 1); // When the offset is at the end of the text node, set it to after the
while (brContent && brContent->IsHTMLElement()) { // text node, to make sure the caret is drawn on a new line when the last
if (!brContent->IsHTMLElement(nsGkAtoms::br) || IsContentBR(brContent)) { // character of the text node is '\n' in <textarea>.
break; int32_t textLength =
} static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength());
brContent = node->GetChildAt(--nodeOffset - 1); 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 nsresult

Просмотреть файл

@ -10,6 +10,8 @@
#include "mozilla/EventForwards.h" #include "mozilla/EventForwards.h"
#include "mozilla/dom/Selection.h" #include "mozilla/dom/Selection.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIFrame.h"
#include "nsINode.h"
#include "nsRange.h" #include "nsRange.h"
class nsPresContext; class nsPresContext;
@ -81,35 +83,161 @@ public:
// FlatText means the text that is generated from DOM tree. The BR elements // FlatText means the text that is generated from DOM tree. The BR elements
// are replaced to native linefeeds. Other elements are ignored. // are replaced to native linefeeds. Other elements are ignored.
// Get the offset in FlatText of the range. (also used by IMEContentObserver) // NodePosition stores a pair of node and offset in the node.
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent, // When mNode is an element and mOffset is 0, the start position means after
nsINode* aNode, // the open tag of mNode.
int32_t aNodeOffset, // This is useful to receive one or more sets of them instead of nsRange.
uint32_t* aOffset, struct NodePosition
LineBreakType aLineBreakType); {
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent, nsCOMPtr<nsINode> mNode;
nsRange* aRange, int32_t mOffset;
uint32_t* aOffset, // Only when mNode is an element node and mOffset is 0, mAfterOpenTag is
LineBreakType aLineBreakType); // 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 // Computes the native text length between aStartOffset and aEndOffset of
// aContent. Currently, this method supports only text node or br element // aContent. aContent must be a text node.
// for aContent.
static uint32_t GetNativeTextLength(nsIContent* aContent, static uint32_t GetNativeTextLength(nsIContent* aContent,
uint32_t aStartOffset, uint32_t aStartOffset,
uint32_t aEndOffset); 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, static uint32_t GetNativeTextLength(nsIContent* aContent,
uint32_t aMaxLength = UINT32_MAX); 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 // Get the text length of a given range of a content node in
// the given line break type. // the given line break type.
static uint32_t GetTextLengthInRange(nsIContent* aContent, static uint32_t GetTextLengthInRange(nsIContent* aContent,
uint32_t aXPStartOffset, uint32_t aXPStartOffset,
uint32_t aXPEndOffset, uint32_t aXPEndOffset,
LineBreakType aLineBreakType); LineBreakType aLineBreakType);
protected: // Get the contents of aRange as plain text.
static uint32_t GetTextLength(nsIContent* aContent, nsresult GenerateFlatTextContent(nsRange* aRange,
LineBreakType aLineBreakType, nsAFlatString& aString,
uint32_t aMaxLength = UINT32_MAX); 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(WidgetQueryContentEvent* aEvent);
static LineBreakType GetLineBreakType(WidgetSelectionEvent* aEvent); static LineBreakType GetLineBreakType(WidgetSelectionEvent* aEvent);
static LineBreakType GetLineBreakType(bool aUseNativeLineBreak); static LineBreakType GetLineBreakType(bool aUseNativeLineBreak);
@ -152,10 +280,10 @@ protected:
int32_t aXPStartOffset, int32_t aXPStartOffset,
int32_t aXPEndOffset, int32_t aXPEndOffset,
LineBreakType aLineBreakType); LineBreakType aLineBreakType);
static nsresult GenerateFlatFontRanges(nsRange* aRange, nsresult GenerateFlatFontRanges(nsRange* aRange,
FontRangeArray& aFontRanges, FontRangeArray& aFontRanges,
uint32_t& aLength, uint32_t& aLength,
LineBreakType aLineBreakType); LineBreakType aLineBreakType);
}; };
} // namespace mozilla } // namespace mozilla

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -28,6 +28,22 @@
#include "nsStyleConsts.h" #include "nsStyleConsts.h"
#include "nsUnicharUtils.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) NS_IMPL_NS_NEW_HTML_ELEMENT(Link)
namespace mozilla { namespace mozilla {
@ -126,6 +142,26 @@ HTMLLinkElement::SetItemValueText(const nsAString& aValue)
SetHref(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 nsresult
HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent, nsIContent* aBindingParent,
@ -145,6 +181,9 @@ HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
if (IsInComposedDoc()) { if (IsInComposedDoc()) {
UpdatePreconnect(); UpdatePreconnect();
if (HasDNSPrefetchRel()) {
TryDNSPrefetch();
}
} }
void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal; void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal;
@ -173,6 +212,12 @@ HTMLLinkElement::LinkRemoved()
void void
HTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent) 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 // If this link is ever reinserted into a document, it might
// be under a different xml:base, so forget the cached state now. // be under a different xml:base, so forget the cached state now.
Link::ResetLinkState(false, Link::ElementHasHref()); 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 nsresult
HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify) 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, UpdateStyleSheetInternal(nullptr, nullptr,
dropSheet || dropSheet ||
(aName == nsGkAtoms::title || (aName == nsGkAtoms::title ||

Просмотреть файл

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

Просмотреть файл

@ -3422,15 +3422,7 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
#endif // MOZ_EME #endif // MOZ_EME
} }
// If this element had a video track, but consists only of an audio track now, mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
// 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);
}
if (IsVideo() && aInfo->HasVideo()) { if (IsVideo() && aInfo->HasVideo()) {
// We are a video element playing video so update the screen wakelock // We are a video element playing video so update the screen wakelock

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -2694,6 +2694,12 @@ TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId, uint64_t* aOutInputBlockId,
nsEventStatus* aOutApzResponse) 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 (AsyncPanZoomEnabled()) {
if (aOutTargetGuid) { if (aOutTargetGuid) {
*aOutTargetGuid = InputAPZContext::GetTargetLayerGuid(); *aOutTargetGuid = InputAPZContext::GetTargetLayerGuid();
@ -2715,10 +2721,6 @@ TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
if (aOutApzResponse) { if (aOutApzResponse) {
*aOutApzResponse = InputAPZContext::GetApzResponse(); *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 { } else {
if (aOutInputBlockId) { if (aOutInputBlockId) {
*aOutInputBlockId = 0; *aOutInputBlockId = 0;

Просмотреть файл

@ -85,16 +85,6 @@ public:
NS_DispatchToMainThread(r); 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 VideoFrameContainer* GetVideoFrameContainer() = 0;
virtual mozilla::layers::ImageContainer* GetImageContainer() = 0; virtual mozilla::layers::ImageContainer* GetImageContainer() = 0;

Просмотреть файл

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

Просмотреть файл

@ -308,7 +308,7 @@ ParseXing(const char *aBuffer)
} }
static int64_t static int64_t
FindNumVBRFrames(const nsAutoCString& aFrame) FindNumVBRFrames(const nsCString& aFrame)
{ {
const char *buffer = aFrame.get(); const char *buffer = aFrame.get();
const char *bufferEnd = aFrame.get() + aFrame.Length(); 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 // 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. // 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 // While we are reading the first frame, this is the stream offset of the
// last byte of that frame. -1 at all other times. // last byte of that frame. -1 at all other times.

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -239,10 +239,6 @@ public:
return &mBuffered; 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) void DispatchSetStartTime(int64_t aStartTime)
{ {
RefPtr<MediaDecoderReader> self = this; RefPtr<MediaDecoderReader> self = this;
@ -279,6 +275,9 @@ public:
return mTimedMetadataEvent; return mTimedMetadataEvent;
} }
// Notified by the OggReader during playback when chained ogg is detected.
MediaEventSource<void>& OnMediaNotSeekable() { return mOnMediaNotSeekable; }
protected: protected:
virtual ~MediaDecoderReader(); virtual ~MediaDecoderReader();
@ -369,6 +368,9 @@ protected:
// Used to send TimedMetadata to the listener. // Used to send TimedMetadata to the listener.
TimedMetadataEventProducer mTimedMetadataEvent; TimedMetadataEventProducer mTimedMetadataEvent;
// Notify if this media is not seekable.
MediaEventProducer<void> mOnMediaNotSeekable;
private: private:
// Does any spinup that needs to happen on this task queue. This runs on a // 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 // 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. // Set mode to PLAYBACK after reading metadata.
mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK); mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
mDecoder->DispatchSetMediaSeekable(mReader->IsMediaSeekable());
mInfo = aMetadata->mInfo; mInfo = aMetadata->mInfo;
mMetadataTags = aMetadata->mTags.forget(); mMetadataTags = aMetadata->mTags.forget();
RefPtr<MediaDecoderStateMachine> self = this; RefPtr<MediaDecoderStateMachine> self = this;

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -49,11 +49,6 @@ public:
RefPtr<SeekPromise> RefPtr<SeekPromise>
Seek(int64_t aTime, int64_t aUnused) override; Seek(int64_t aTime, int64_t aUnused) override;
bool IsMediaSeekable() override
{
return mSeekable;
}
protected: protected:
void NotifyDataArrivedInternal() override; void NotifyDataArrivedInternal() override;
@ -396,9 +391,6 @@ private:
// True if we've read the streams' metadata. // True if we've read the streams' metadata.
bool mInitDone; bool mInitDone;
MozPromiseHolder<MetadataPromise> mMetadataPromise; 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() bool IsEncrypted()
{ {
return mIsEncrypted; return mIsEncrypted;

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -119,7 +119,16 @@ GMPContentChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
void* session = nullptr; void* session = nullptr;
GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &session); GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &session);
if (err != GMPNoErr || !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)); child->Init(static_cast<GMPDecryptor*>(session));

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -244,7 +244,9 @@ enum GMPSessionType {
kGMPSessionInvalid = 2 // Must always be last. 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. // API exposed by plugin library to manage decryption sessions.
// When the Host requests this by calling GMPGetAPIFunc(). // When the Host requests this by calling GMPGetAPIFunc().

Просмотреть файл

@ -468,12 +468,15 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
/* report the duration */ /* report the duration */
gint64 duration; gint64 duration;
bool isMediaSeekable = false;
if (isMP3 && mMP3FrameParser.IsMP3()) { if (isMP3 && mMP3FrameParser.IsMP3()) {
// The MP3FrameParser has reported a duration; use that over the gstreamer // The MP3FrameParser has reported a duration; use that over the gstreamer
// reported duration for inter-platform consistency. // reported duration for inter-platform consistency.
mUseParserDuration = true; mUseParserDuration = true;
mLastParserDuration = mMP3FrameParser.GetDuration(); mLastParserDuration = mMP3FrameParser.GetDuration();
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(mLastParserDuration)); mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(mLastParserDuration));
isMediaSeekable = true;
} else { } else {
LOG(LogLevel::Debug, "querying duration"); LOG(LogLevel::Debug, "querying duration");
// Otherwise use the gstreamer 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)); LOG(LogLevel::Debug, "have duration %" GST_TIME_FORMAT, GST_TIME_ARGS(duration));
duration = GST_TIME_AS_USECONDS (duration); duration = GST_TIME_AS_USECONDS (duration);
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration)); mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration));
isMediaSeekable = true;
} }
} }
mInfo.mMediaSeekable = isMediaSeekable;
int n_video = 0, n_audio = 0; int n_video = 0, n_audio = 0;
g_object_get(mPlayBin, "n-video", &n_video, "n-audio", &n_audio, nullptr); 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; 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() nsresult GStreamerReader::CheckSupportedFormats()
{ {
bool done = false; bool done = false;

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -310,6 +310,8 @@ void MediaOmxReader::HandleResourceAllocated()
mInfo.mAudio.mRate = sampleRate; mInfo.mAudio.mRate = sampleRate;
} }
mInfo.mMediaSeekable = mExtractor->flags() & MediaExtractor::CAN_SEEK;
RefPtr<MetadataHolder> metadata = new MetadataHolder(); RefPtr<MetadataHolder> metadata = new MetadataHolder();
metadata->mInfo = mInfo; metadata->mInfo = mInfo;
metadata->mTags = nullptr; metadata->mTags = nullptr;
@ -321,13 +323,6 @@ void MediaOmxReader::HandleResourceAllocated()
mMetadataPromise.Resolve(metadata, __func__); 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, bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold) int64_t aTimeThreshold)
{ {

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -33,8 +33,6 @@ public:
virtual media::TimeIntervals GetBuffered() override; virtual media::TimeIntervals GetBuffered() override;
virtual bool IsMediaSeekable() override;
private: private:
bool ReadFromResource(uint8_t *aBuf, uint32_t aLength); 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 skip-if = buildapp == 'b2g' || toolkit == 'android' # no windowshare on b2g/android # Bug 1141029 Mulet parity with B2G Desktop for TC
[test_getUserMedia_basicVideoAudio.html] [test_getUserMedia_basicVideoAudio.html]
skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure, turned an intermittent (bug 962579) into a permanant orange 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_constraints.html]
[test_getUserMedia_callbacks.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. skip-if = toolkit == 'gonk' || buildapp == 'mulet' # Bug 1063290, intermittent timeout # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.

Просмотреть файл

@ -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; return NS_OK;
} }
bool
WaveReader::IsMediaSeekable()
{
// not used
return true;
}
template <typename T> T UnsignedByteToAudioSample(uint8_t aValue); template <typename T> T UnsignedByteToAudioSample(uint8_t aValue);
template <typename T> T SignedShortToAudioSample(int16_t aValue); template <typename T> T SignedShortToAudioSample(int16_t aValue);

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше