зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
5adcabb340
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче