зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to central, a=merge CLOSED TREE
This commit is contained in:
Коммит
5c26bde4f0
|
@ -167,7 +167,9 @@ SelectionManager::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
|
|||
nsISelection* aSelection,
|
||||
int16_t aReason)
|
||||
{
|
||||
NS_ENSURE_ARG(aDOMDocument);
|
||||
if (NS_WARN_IF(!aDOMDocument) || NS_WARN_IF(!aSelection)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> documentNode(do_QueryInterface(aDOMDocument));
|
||||
DocAccessible* document = GetAccService()->GetDocAccessible(documentNode);
|
||||
|
@ -182,7 +184,7 @@ SelectionManager::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
|
|||
// so that we are guaranteed that the notification is processed before
|
||||
// the selection manager is destroyed.
|
||||
RefPtr<SelData> selData =
|
||||
new SelData(static_cast<Selection*>(aSelection), aReason);
|
||||
new SelData(aSelection->AsSelection(), aReason);
|
||||
document->HandleNotification<SelectionManager, SelData>
|
||||
(this, &SelectionManager::ProcessSelectionChanged, selData);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[DEFAULT]
|
||||
skip-if = os != "linux"
|
||||
|
||||
[browser_420786.js]
|
||||
[browser_633221.js]
|
||||
skip-if = os != "linux"
|
||||
|
||||
|
|
|
@ -72,10 +72,6 @@ function onPageLoad() {
|
|||
function test() {
|
||||
var osString = Cc["@mozilla.org/xre/app-info;1"].
|
||||
getService(Ci.nsIXULRuntime).OS;
|
||||
if (osString != "Linux") {
|
||||
todo(false, "This test is Linux specific for now.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// If GSettings is available, then the GConf tests
|
||||
|
|
|
@ -554,27 +554,17 @@ nsCopySupport::GetSelectionForCopy(nsIDocument* aDocument, nsISelection** aSelec
|
|||
if (!presShell)
|
||||
return nullptr;
|
||||
|
||||
// check if the focused node in the window has a selection
|
||||
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
|
||||
nsIContent* content =
|
||||
nsFocusManager::GetFocusedDescendant(aDocument->GetWindow(), false,
|
||||
getter_AddRefs(focusedWindow));
|
||||
if (content) {
|
||||
nsIFrame* frame = content->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
nsCOMPtr<nsISelectionController> selCon;
|
||||
frame->GetSelectionController(presShell->GetPresContext(), getter_AddRefs(selCon));
|
||||
if (selCon) {
|
||||
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection);
|
||||
return content;
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIContent> focusedContent;
|
||||
nsCOMPtr<nsISelectionController> selectionController =
|
||||
presShell->GetSelectionControllerForFocusedContent(
|
||||
getter_AddRefs(focusedContent));
|
||||
if (!selectionController) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// if no selection was found, use the main selection for the window
|
||||
NS_IF_ADDREF(*aSelection =
|
||||
presShell->GetCurrentSelection(SelectionType::eNormal));
|
||||
return nullptr;
|
||||
selectionController->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
aSelection);
|
||||
return focusedContent;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -1918,6 +1918,56 @@ nsDOMWindowUtils::SendQueryContentEvent(uint32_t aType,
|
|||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
SelectionType selectionType = SelectionType::eNormal;
|
||||
static const uint32_t kSelectionFlags =
|
||||
QUERY_CONTENT_FLAG_SELECTION_SPELLCHECK |
|
||||
QUERY_CONTENT_FLAG_SELECTION_IME_RAWINPUT |
|
||||
QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDRAWTEXT |
|
||||
QUERY_CONTENT_FLAG_SELECTION_IME_CONVERTEDTEXT |
|
||||
QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDCONVERTEDTEXT |
|
||||
QUERY_CONTENT_FLAG_SELECTION_ACCESSIBILITY |
|
||||
QUERY_CONTENT_FLAG_SELECTION_FIND |
|
||||
QUERY_CONTENT_FLAG_SELECTION_URLSECONDARY |
|
||||
QUERY_CONTENT_FLAG_SELECTION_URLSTRIKEOUT;
|
||||
switch (aAdditionalFlags & kSelectionFlags) {
|
||||
case QUERY_CONTENT_FLAG_SELECTION_SPELLCHECK:
|
||||
selectionType = SelectionType::eSpellCheck;
|
||||
break;
|
||||
case QUERY_CONTENT_FLAG_SELECTION_IME_RAWINPUT:
|
||||
selectionType = SelectionType::eIMERawClause;
|
||||
break;
|
||||
case QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDRAWTEXT:
|
||||
selectionType = SelectionType::eIMESelectedRawClause;
|
||||
break;
|
||||
case QUERY_CONTENT_FLAG_SELECTION_IME_CONVERTEDTEXT:
|
||||
selectionType = SelectionType::eIMEConvertedClause;
|
||||
break;
|
||||
case QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDCONVERTEDTEXT:
|
||||
selectionType = SelectionType::eIMESelectedClause;
|
||||
break;
|
||||
case QUERY_CONTENT_FLAG_SELECTION_ACCESSIBILITY:
|
||||
selectionType = SelectionType::eAccessibility;
|
||||
break;
|
||||
case QUERY_CONTENT_FLAG_SELECTION_FIND:
|
||||
selectionType = SelectionType::eFind;
|
||||
break;
|
||||
case QUERY_CONTENT_FLAG_SELECTION_URLSECONDARY:
|
||||
selectionType = SelectionType::eURLSecondary;
|
||||
break;
|
||||
case QUERY_CONTENT_FLAG_SELECTION_URLSTRIKEOUT:
|
||||
selectionType = SelectionType::eURLStrikeout;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (selectionType != SelectionType::eNormal &&
|
||||
message != eQuerySelectedText) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWidget> targetWidget = widget;
|
||||
LayoutDeviceIntPoint pt(aX, aY);
|
||||
|
||||
|
@ -1962,6 +2012,9 @@ nsDOMWindowUtils::SendQueryContentEvent(uint32_t aType,
|
|||
case eQueryTextRect:
|
||||
queryEvent.InitForQueryTextRect(aOffset, aLength, useNativeLineBreak);
|
||||
break;
|
||||
case eQuerySelectedText:
|
||||
queryEvent.InitForQuerySelectedText(selectionType, useNativeLineBreak);
|
||||
break;
|
||||
default:
|
||||
queryEvent.mUseNativeLineBreak = useNativeLineBreak;
|
||||
break;
|
||||
|
|
|
@ -1412,7 +1412,7 @@ nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection)
|
|||
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
nsCOMPtr<nsIDOMNode> commonParent;
|
||||
Selection* selection = static_cast<Selection*>(aSelection);
|
||||
Selection* selection = aSelection->AsSelection();
|
||||
uint32_t rangeCount = selection->RangeCount();
|
||||
|
||||
// if selection is uninitialized return
|
||||
|
|
|
@ -9427,9 +9427,9 @@ nsGlobalWindow::GetSelectionOuter()
|
|||
if (!presShell) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return static_cast<Selection*>(
|
||||
presShell->GetCurrentSelection(SelectionType::eNormal));
|
||||
nsISelection* domSelection =
|
||||
presShell->GetCurrentSelection(SelectionType::eNormal);
|
||||
return domSelection ? domSelection->AsSelection() : nullptr;
|
||||
}
|
||||
|
||||
Selection*
|
||||
|
|
|
@ -11,6 +11,14 @@ interface nsIDOMNode;
|
|||
interface nsIDOMRange;
|
||||
interface nsINode;
|
||||
|
||||
%{C++
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Selection;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
%}
|
||||
|
||||
/**
|
||||
* Interface for manipulating and querying the current selected range
|
||||
* of nodes within the document.
|
||||
|
@ -150,4 +158,13 @@ interface nsISelection : nsISupports
|
|||
*/
|
||||
void modify(in DOMString alter, in DOMString direction,
|
||||
in DOMString granularity);
|
||||
|
||||
%{C++
|
||||
/**
|
||||
* AsSelection() returns a pointer to Selection class if the instance is
|
||||
* derived from it. Otherwise, nullptr but it should never happen
|
||||
* since Selection is the only class implementing nsISelection.
|
||||
*/
|
||||
virtual mozilla::dom::Selection* AsSelection() = 0;
|
||||
%}
|
||||
};
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "nsIContentIterator.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsISelection.h"
|
||||
#include "nsISelectionController.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIObjectFrame.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
@ -126,27 +125,21 @@ ContentEventHandler::InitBasic()
|
|||
}
|
||||
|
||||
nsresult
|
||||
ContentEventHandler::InitCommon()
|
||||
ContentEventHandler::InitRootContent(Selection* aNormalSelection)
|
||||
{
|
||||
if (mSelection) {
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_ASSERT(aNormalSelection);
|
||||
|
||||
nsresult rv = InitBasic();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Root content should be computed with normal selection because normal
|
||||
// selection is typically has at least one range but the other selections
|
||||
// not so. If there is a range, computing its root is easy, but if
|
||||
// there are no ranges, we need to use ancestor limit instead.
|
||||
MOZ_ASSERT(aNormalSelection->Type() == SelectionType::eNormal);
|
||||
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
nsCopySupport::GetSelectionForCopy(mPresShell->GetDocument(),
|
||||
getter_AddRefs(sel));
|
||||
mSelection = static_cast<Selection*>(sel.get());
|
||||
if (NS_WARN_IF(!mSelection)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!mSelection->RangeCount()) {
|
||||
if (!aNormalSelection->RangeCount()) {
|
||||
// If there is no selection range, we should compute the selection root
|
||||
// from ancestor limiter or root content of the document.
|
||||
rv = mSelection->GetAncestorLimiter(getter_AddRefs(mRootContent));
|
||||
nsresult rv =
|
||||
aNormalSelection->GetAncestorLimiter(getter_AddRefs(mRootContent));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -156,8 +149,116 @@ ContentEventHandler::InitCommon()
|
|||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Assume that there is selection at beginning of the root content.
|
||||
RefPtr<nsRange> range(aNormalSelection->GetRangeAt(0));
|
||||
if (NS_WARN_IF(!range)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// If there is a selection, we should retrieve the selection root from
|
||||
// the range since when the window is inactivated, the ancestor limiter
|
||||
// of selection was cleared by blur event handler of nsEditor but the
|
||||
// selection range still keeps storing the nodes. If the active element of
|
||||
// the deactive window is <input> or <textarea>, we can compute the
|
||||
// selection root from them.
|
||||
nsINode* startNode = range->GetStartParent();
|
||||
nsINode* endNode = range->GetEndParent();
|
||||
if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// See bug 537041 comment 5, the range could have removed node.
|
||||
if (NS_WARN_IF(startNode->GetUncomposedDoc() != mPresShell->GetDocument())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_ASSERTION(startNode->GetUncomposedDoc() == endNode->GetUncomposedDoc(),
|
||||
"firstNormalSelectionRange crosses the document boundary");
|
||||
|
||||
mRootContent = startNode->GetSelectionRootContent(mPresShell);
|
||||
if (NS_WARN_IF(!mRootContent)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentEventHandler::InitCommon(SelectionType aSelectionType)
|
||||
{
|
||||
if (mSelection && mSelection->Type() == aSelectionType) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mSelection = nullptr;
|
||||
mFirstSelectedRange = nullptr;
|
||||
mRootContent = nullptr;
|
||||
|
||||
nsresult rv = InitBasic();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISelectionController> selectionController =
|
||||
mPresShell->GetSelectionControllerForFocusedContent();
|
||||
if (NS_WARN_IF(!selectionController)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
rv = selectionController->GetSelection(ToRawSelectionType(aSelectionType),
|
||||
getter_AddRefs(selection));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mSelection = static_cast<Selection*>(selection.get());
|
||||
if (NS_WARN_IF(!mSelection)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
RefPtr<Selection> normalSelection;
|
||||
if (mSelection->Type() == SelectionType::eNormal) {
|
||||
normalSelection = mSelection;
|
||||
} else {
|
||||
nsCOMPtr<nsISelection> domSelection;
|
||||
nsresult rv =
|
||||
selectionController->GetSelection(
|
||||
nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(domSelection));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
if (NS_WARN_IF(!domSelection)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
normalSelection = domSelection->AsSelection();
|
||||
if (NS_WARN_IF(!normalSelection)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
rv = InitRootContent(normalSelection);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mSelection->RangeCount()) {
|
||||
mFirstSelectedRange = mSelection->GetRangeAt(0);
|
||||
if (NS_WARN_IF(!mFirstSelectedRange)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Even if there are no selection ranges, it's usual case if aSelectionType
|
||||
// is a special selection.
|
||||
if (aSelectionType != SelectionType::eNormal) {
|
||||
MOZ_ASSERT(!mFirstSelectedRange);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// But otherwise, we need to assume that there is a selection range at the
|
||||
// beginning of the root content if aSelectionType is eNormal.
|
||||
rv = nsRange::CreateRange(mRootContent, 0, mRootContent, 0,
|
||||
getter_AddRefs(mFirstSelectedRange));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!mFirstSelectedRange)) {
|
||||
|
@ -166,39 +267,23 @@ ContentEventHandler::InitCommon()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
mFirstSelectedRange = mSelection->GetRangeAt(0);
|
||||
if (NS_WARN_IF(!mFirstSelectedRange)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// If there is a selection, we should retrieve the selection root from
|
||||
// the range since when the window is inactivated, the ancestor limiter
|
||||
// of mSelection was cleared by blur event handler of nsEditor but the
|
||||
// selection range still keeps storing the nodes. If the active element of
|
||||
// the deactive window is <input> or <textarea>, we can compute the selection
|
||||
// root from them.
|
||||
nsINode* startNode = mFirstSelectedRange->GetStartParent();
|
||||
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
|
||||
nsINode* endNode = mFirstSelectedRange->GetEndParent();
|
||||
NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
|
||||
|
||||
// See bug 537041 comment 5, the range could have removed node.
|
||||
NS_ENSURE_TRUE(startNode->GetUncomposedDoc() == mPresShell->GetDocument(),
|
||||
NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ASSERTION(startNode->GetUncomposedDoc() == endNode->GetUncomposedDoc(),
|
||||
"mFirstSelectedRange crosses the document boundary");
|
||||
|
||||
mRootContent = startNode->GetSelectionRootContent(mPresShell);
|
||||
NS_ENSURE_TRUE(mRootContent, NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentEventHandler::Init(WidgetQueryContentEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(aEvent, "aEvent must not be null");
|
||||
MOZ_ASSERT(aEvent->mMessage == eQuerySelectedText ||
|
||||
aEvent->mInput.mSelectionType == SelectionType::eNormal);
|
||||
|
||||
nsresult rv = InitCommon();
|
||||
// Note that we should ignore WidgetQueryContentEvent::Input::mSelectionType
|
||||
// if the event isn't eQuerySelectedText.
|
||||
SelectionType selectionType =
|
||||
aEvent->mMessage == eQuerySelectedText ? aEvent->mInput.mSelectionType :
|
||||
SelectionType::eNormal;
|
||||
if (NS_WARN_IF(selectionType == SelectionType::eNone)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = InitCommon(selectionType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aEvent->mSucceeded = false;
|
||||
|
@ -1122,6 +1207,15 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
|
|||
return rv;
|
||||
}
|
||||
|
||||
if (!mFirstSelectedRange) {
|
||||
MOZ_ASSERT(aEvent->mInput.mSelectionType != SelectionType::eNormal);
|
||||
MOZ_ASSERT(aEvent->mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND);
|
||||
MOZ_ASSERT(aEvent->mReply.mString.IsEmpty());
|
||||
MOZ_ASSERT(!aEvent->mReply.mHasSelection);
|
||||
aEvent->mSucceeded = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsINode* const startNode = mFirstSelectedRange->GetStartParent();
|
||||
nsINode* const endNode = mFirstSelectedRange->GetEndParent();
|
||||
|
||||
|
@ -1142,6 +1236,10 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
|
|||
nsCOMPtr<nsINode> anchorNode, focusNode;
|
||||
int32_t anchorOffset, focusOffset;
|
||||
if (mSelection->RangeCount()) {
|
||||
// If there is only one selection range, the anchor/focus node and offset
|
||||
// are the information of the range. Therefore, we have the direction
|
||||
// information.
|
||||
if (mSelection->RangeCount() == 1) {
|
||||
anchorNode = mSelection->GetAnchorNode();
|
||||
focusNode = mSelection->GetFocusNode();
|
||||
if (NS_WARN_IF(!anchorNode) || NS_WARN_IF(!focusNode)) {
|
||||
|
@ -1156,23 +1254,15 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
|
|||
int16_t compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
|
||||
focusNode, focusOffset);
|
||||
aEvent->mReply.mReversed = compare > 0;
|
||||
}
|
||||
// However, if there are 2 or more selection ranges, we have no information
|
||||
// of that.
|
||||
else {
|
||||
aEvent->mReply.mReversed = false;
|
||||
}
|
||||
|
||||
if (compare) {
|
||||
RefPtr<nsRange> range;
|
||||
if (mSelection->RangeCount() == 1) {
|
||||
range = mFirstSelectedRange;
|
||||
} else {
|
||||
rv = nsRange::CreateRange(anchorNode, anchorOffset,
|
||||
focusNode, focusOffset,
|
||||
getter_AddRefs(range));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
if (NS_WARN_IF(!range)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
rv = GenerateFlatTextContent(range, aEvent->mReply.mString,
|
||||
if (!mFirstSelectedRange->Collapsed()) {
|
||||
rv = GenerateFlatTextContent(mFirstSelectedRange, aEvent->mReply.mString,
|
||||
lineBreakType);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
|
@ -1981,7 +2071,7 @@ ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent)
|
|||
nsresult rv =
|
||||
IMEStateManager::GetFocusSelectionAndRoot(getter_AddRefs(sel),
|
||||
getter_AddRefs(mRootContent));
|
||||
mSelection = static_cast<Selection*>(sel.get());
|
||||
mSelection = sel ? sel->AsSelection() : nullptr;
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsISelectionController.h"
|
||||
#include "nsRange.h"
|
||||
|
||||
class nsPresContext;
|
||||
|
@ -69,7 +70,14 @@ public:
|
|||
protected:
|
||||
nsPresContext* mPresContext;
|
||||
nsCOMPtr<nsIPresShell> mPresShell;
|
||||
// mSelection is typically normal selection but if OnQuerySelectedText()
|
||||
// is called, i.e., handling eQuerySelectedText, it's the specified selection
|
||||
// by WidgetQueryContentEvent::mInput::mSelectionType.
|
||||
RefPtr<Selection> mSelection;
|
||||
// mFirstSelectedRange is the first selected range of mSelection. If
|
||||
// mSelection is normal selection, this must not be nullptr if Init()
|
||||
// succeed. Otherwise, this may be nullptr if there are no selection
|
||||
// ranges.
|
||||
RefPtr<nsRange> mFirstSelectedRange;
|
||||
nsCOMPtr<nsIContent> mRootContent;
|
||||
|
||||
|
@ -77,7 +85,14 @@ protected:
|
|||
nsresult Init(WidgetSelectionEvent* aEvent);
|
||||
|
||||
nsresult InitBasic();
|
||||
nsresult InitCommon();
|
||||
nsresult InitCommon(SelectionType aSelectionType = SelectionType::eNormal);
|
||||
/**
|
||||
* InitRootContent() computes the root content of current focused editor.
|
||||
*
|
||||
* @param aNormalSelection This must be a Selection instance whose type is
|
||||
* SelectionType::eNormal.
|
||||
*/
|
||||
nsresult InitRootContent(Selection* aNormalSelection);
|
||||
|
||||
public:
|
||||
// FlatText means the text that is generated from DOM tree. The BR elements
|
||||
|
|
|
@ -700,7 +700,8 @@ IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
|
|||
nsresult
|
||||
IMEContentObserver::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
|
||||
{
|
||||
// If the instance has cache, it should use the cached selection which was
|
||||
// If the instance has normal selection cache and the query event queries
|
||||
// normal selection's range, it should use the cached selection which was
|
||||
// sent to the widget. However, if this instance has already received new
|
||||
// selection change notification but hasn't updated the cache yet (i.e.,
|
||||
// not sending selection change notification to IME, don't use the cached
|
||||
|
@ -708,6 +709,7 @@ IMEContentObserver::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
|
|||
// selection cache here, IMENotificationSender won't notify IME of selection
|
||||
// change because it looks like that the selection isn't actually changed.
|
||||
if (aEvent->mMessage == eQuerySelectedText && aEvent->mUseNativeLineBreak &&
|
||||
aEvent->mInput.mSelectionType == SelectionType::eNormal &&
|
||||
mSelectionData.IsValid() && !mNeedsToNotifyIMEOfSelectionChange) {
|
||||
aEvent->mReply.mContentsRoot = mRootContent;
|
||||
aEvent->mReply.mHasSelection = !mSelectionData.IsCollapsed();
|
||||
|
|
|
@ -221,6 +221,39 @@ IMEStateManager::StopIMEStateManagement()
|
|||
DestroyIMEContentObserver();
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
IMEStateManager::MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget,
|
||||
uint32_t aStartOffset)
|
||||
{
|
||||
if (NS_WARN_IF(!sTextCompositions)) {
|
||||
MOZ_LOG(sISMLog, LogLevel::Warning,
|
||||
("ISM: IMEStateManager::MaybeStartOffsetUpdatedInChild("
|
||||
"aWidget=0x%p, aStartOffset=%u), called when there is no "
|
||||
"composition", aWidget, aStartOffset));
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<TextComposition> composition = GetTextCompositionFor(aWidget);
|
||||
if (NS_WARN_IF(!composition)) {
|
||||
MOZ_LOG(sISMLog, LogLevel::Warning,
|
||||
("ISM: IMEStateManager::MaybeStartOffsetUpdatedInChild("
|
||||
"aWidget=0x%p, aStartOffset=%u), called when there is no "
|
||||
"composition", aWidget, aStartOffset));
|
||||
return;
|
||||
}
|
||||
|
||||
if (composition->NativeOffsetOfStartComposition() == aStartOffset) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_LOG(sISMLog, LogLevel::Info,
|
||||
("ISM: IMEStateManager::MaybeStartOffsetUpdatedInChild("
|
||||
"aWidget=0x%p, aStartOffset=%u), old offset=%u",
|
||||
aWidget, aStartOffset, composition->NativeOffsetOfStartComposition()));
|
||||
composition->OnStartOffsetUpdatedInChild(aStartOffset);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
|
||||
|
|
|
@ -97,6 +97,18 @@ public:
|
|||
*/
|
||||
static void StopIMEStateManagement();
|
||||
|
||||
/**
|
||||
* MaybeStartOffsetUpdatedInChild() is called when composition start offset
|
||||
* is maybe updated in the child process. I.e., even if it's not updated,
|
||||
* this is called and never called if the composition is in this process.
|
||||
* @param aWidget The widget whose native IME context has the
|
||||
* composition.
|
||||
* @param aStartOffset New composition start offset with native
|
||||
* linebreaks.
|
||||
*/
|
||||
static void MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget,
|
||||
uint32_t aStartOffset);
|
||||
|
||||
static nsresult OnDestroyPresContext(nsPresContext* aPresContext);
|
||||
static nsresult OnRemoveContent(nsPresContext* aPresContext,
|
||||
nsIContent* aContent);
|
||||
|
|
|
@ -58,7 +58,7 @@ TextComposition::TextComposition(nsPresContext* aPresContext,
|
|||
, mTabParent(aTabParent)
|
||||
, mNativeContext(aCompositionEvent->mNativeIMEContext)
|
||||
, mCompositionStartOffset(0)
|
||||
, mCompositionTargetOffset(0)
|
||||
, mTargetClauseOffsetInComposition(0)
|
||||
, mIsSynthesizedForTests(aCompositionEvent->mFlags.mIsSynthesizedForTests)
|
||||
, mIsComposing(false)
|
||||
, mIsEditorHandlingEvent(false)
|
||||
|
@ -69,6 +69,7 @@ TextComposition::TextComposition(nsPresContext* aPresContext,
|
|||
, mAllowControlCharacters(
|
||||
Preferences::GetBool("dom.compositionevent.allow_control_characters",
|
||||
false))
|
||||
, mWasCompositionStringEmpty(true)
|
||||
{
|
||||
MOZ_ASSERT(aCompositionEvent->mNativeIMEContext.IsValid());
|
||||
}
|
||||
|
@ -152,6 +153,8 @@ TextComposition::DispatchEvent(WidgetCompositionEvent* aDispatchEvent,
|
|||
|
||||
EventDispatcher::Dispatch(mNode, mPresContext,
|
||||
aDispatchEvent, nullptr, aStatus, aCallBack);
|
||||
|
||||
OnCompositionEventDispatched(aDispatchEvent);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -241,6 +244,8 @@ TextComposition::DispatchCompositionEvent(
|
|||
EventDispatchingCallback* aCallBack,
|
||||
bool aIsSynthesized)
|
||||
{
|
||||
mWasCompositionStringEmpty = mString.IsEmpty();
|
||||
|
||||
// If the content is a container of TabParent, composition should be in the
|
||||
// remote process.
|
||||
if (mTabParent) {
|
||||
|
@ -402,7 +407,7 @@ TextComposition::DispatchCompositionEvent(
|
|||
MOZ_ASSERT(!HasEditor(), "Why does the editor still keep to hold this?");
|
||||
}
|
||||
|
||||
OnCompositionEventHandled(aCompositionEvent);
|
||||
MaybeNotifyIMEOfCompositionEventHandled(aCompositionEvent);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -429,34 +434,93 @@ TextComposition::HandleSelectionEvent(nsPresContext* aPresContext,
|
|||
handler.OnSelectionEvent(aSelectionEvent);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TextComposition::GetSelectionStartOffset()
|
||||
{
|
||||
nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
|
||||
WidgetQueryContentEvent selectedTextEvent(true, eQuerySelectedText, widget);
|
||||
if (mRanges && mRanges->HasClauses()) {
|
||||
selectedTextEvent.InitForQuerySelectedText(
|
||||
ToSelectionType(mRanges->GetFirstClause()->mRangeType));
|
||||
} else {
|
||||
selectedTextEvent.InitForQuerySelectedText(SelectionType::eNormal);
|
||||
}
|
||||
|
||||
// The editor which has this composition is observed by active
|
||||
// IMEContentObserver, we can use the cache of it.
|
||||
RefPtr<IMEContentObserver> contentObserver =
|
||||
IMEStateManager::GetActiveContentObserver();
|
||||
bool doQuerySelection = true;
|
||||
if (contentObserver) {
|
||||
if (contentObserver->IsManaging(this)) {
|
||||
doQuerySelection = false;
|
||||
contentObserver->HandleQueryContentEvent(&selectedTextEvent);
|
||||
}
|
||||
// If another editor already has focus, we cannot retrieve selection
|
||||
// in the editor which has this composition...
|
||||
else if (NS_WARN_IF(contentObserver->GetPresContext() == mPresContext)) {
|
||||
return 0; // XXX Is this okay?
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, using slow path (i.e., compute every time with
|
||||
// ContentEventHandler)
|
||||
if (doQuerySelection) {
|
||||
ContentEventHandler handler(mPresContext);
|
||||
handler.HandleQueryContentEvent(&selectedTextEvent);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!selectedTextEvent.mSucceeded)) {
|
||||
return 0; // XXX Is this okay?
|
||||
}
|
||||
return selectedTextEvent.mReply.mOffset;
|
||||
}
|
||||
|
||||
void
|
||||
TextComposition::OnCompositionEventHandled(
|
||||
TextComposition::OnCompositionEventDispatched(
|
||||
const WidgetCompositionEvent* aCompositionEvent)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mTabParent);
|
||||
|
||||
nsEventStatus status;
|
||||
|
||||
// When compositon start, notify the rect of first offset character.
|
||||
// When not compositon start, notify the rect of selected composition
|
||||
// string if compositionchange event.
|
||||
if (aCompositionEvent->mMessage == eCompositionStart) {
|
||||
nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
|
||||
// Update composition start offset
|
||||
WidgetQueryContentEvent selectedTextEvent(true, eQuerySelectedText, widget);
|
||||
widget->DispatchEvent(&selectedTextEvent, status);
|
||||
if (selectedTextEvent.mSucceeded) {
|
||||
mCompositionStartOffset = selectedTextEvent.mReply.mOffset;
|
||||
} else {
|
||||
// Unknown offset
|
||||
NS_WARNING("Cannot get start offset of IME composition");
|
||||
mCompositionStartOffset = 0;
|
||||
if (!IsValidStateForComposition(aCompositionEvent->mWidget)) {
|
||||
return;
|
||||
}
|
||||
mCompositionTargetOffset = mCompositionStartOffset;
|
||||
} else if (aCompositionEvent->CausesDOMTextEvent()) {
|
||||
mCompositionTargetOffset =
|
||||
mCompositionStartOffset + aCompositionEvent->TargetClauseOffset();
|
||||
} else {
|
||||
|
||||
// Every composition event may cause changing composition start offset,
|
||||
// especially when there is no composition string. Therefore, we need to
|
||||
// update mCompositionStartOffset with the latest offset.
|
||||
|
||||
MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart ||
|
||||
mWasCompositionStringEmpty,
|
||||
"mWasCompositionStringEmpty should be true if the dispatched "
|
||||
"event is eCompositionStart");
|
||||
|
||||
if (mWasCompositionStringEmpty &&
|
||||
!aCompositionEvent->CausesDOMCompositionEndEvent()) {
|
||||
// If there was no composition string, current selection start may be the
|
||||
// offset for inserting composition string.
|
||||
// Update composition start offset with current selection start.
|
||||
mCompositionStartOffset = GetSelectionStartOffset();
|
||||
mTargetClauseOffsetInComposition = 0;
|
||||
}
|
||||
|
||||
if (aCompositionEvent->CausesDOMTextEvent()) {
|
||||
mTargetClauseOffsetInComposition = aCompositionEvent->TargetClauseOffset();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TextComposition::OnStartOffsetUpdatedInChild(uint32_t aStartOffset)
|
||||
{
|
||||
mCompositionStartOffset = aStartOffset;
|
||||
}
|
||||
|
||||
void
|
||||
TextComposition::MaybeNotifyIMEOfCompositionEventHandled(
|
||||
const WidgetCompositionEvent* aCompositionEvent)
|
||||
{
|
||||
if (aCompositionEvent->mMessage != eCompositionStart &&
|
||||
!aCompositionEvent->CausesDOMTextEvent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -111,9 +111,12 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* the offset of first selected clause or start of of compositon
|
||||
* the offset of first selected clause or start of composition
|
||||
*/
|
||||
uint32_t OffsetOfTargetClause() const { return mCompositionTargetOffset; }
|
||||
uint32_t NativeOffsetOfTargetClause() const
|
||||
{
|
||||
return mCompositionStartOffset + mTargetClauseOffsetInComposition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is non-empty composition string and it's not fixed.
|
||||
|
@ -216,9 +219,9 @@ private:
|
|||
|
||||
// Offset of the composition string from start of the editor
|
||||
uint32_t mCompositionStartOffset;
|
||||
// Offset of the selected clause of the composition string from start of the
|
||||
// editor
|
||||
uint32_t mCompositionTargetOffset;
|
||||
// Offset of the selected clause of the composition string from
|
||||
// mCompositionStartOffset
|
||||
uint32_t mTargetClauseOffsetInComposition;
|
||||
|
||||
// See the comment for IsSynthesizedForTests().
|
||||
bool mIsSynthesizedForTests;
|
||||
|
@ -255,12 +258,16 @@ private:
|
|||
// and compositionend events.
|
||||
bool mAllowControlCharacters;
|
||||
|
||||
// mWasCompositionStringEmpty is true if the composition string was empty
|
||||
// when DispatchCompositionEvent() is called.
|
||||
bool mWasCompositionStringEmpty;
|
||||
|
||||
// Hide the default constructor and copy constructor.
|
||||
TextComposition()
|
||||
: mPresContext(nullptr)
|
||||
, mNativeContext(nullptr)
|
||||
, mCompositionStartOffset(0)
|
||||
, mCompositionTargetOffset(0)
|
||||
, mTargetClauseOffsetInComposition(0)
|
||||
, mIsSynthesizedForTests(false)
|
||||
, mIsComposing(false)
|
||||
, mIsEditorHandlingEvent(false)
|
||||
|
@ -269,6 +276,7 @@ private:
|
|||
, mRequestedToCommitOrCancel(false)
|
||||
, mWasNativeCompositionEndEventDiscarded(false)
|
||||
, mAllowControlCharacters(false)
|
||||
, mWasCompositionStringEmpty(true)
|
||||
{}
|
||||
TextComposition(const TextComposition& aOther);
|
||||
|
||||
|
@ -372,11 +380,36 @@ private:
|
|||
void OnCompositionEventDiscarded(WidgetCompositionEvent* aCompositionEvent);
|
||||
|
||||
/**
|
||||
* Calculate composition offset then notify composition update to widget
|
||||
* OnCompositionEventDispatched() is called after a composition event is
|
||||
* dispatched.
|
||||
*/
|
||||
void OnCompositionEventHandled(
|
||||
void OnCompositionEventDispatched(
|
||||
const WidgetCompositionEvent* aDispatchEvent);
|
||||
|
||||
/**
|
||||
* MaybeNotifyIMEOfCompositionEventHandled() notifies IME of composition
|
||||
* event handled. This should be called after dispatching a composition
|
||||
* event which came from widget.
|
||||
*/
|
||||
void MaybeNotifyIMEOfCompositionEventHandled(
|
||||
const WidgetCompositionEvent* aCompositionEvent);
|
||||
|
||||
/**
|
||||
* GetSelectionStartOffset() returns normal selection start offset in the
|
||||
* editor which has this composition.
|
||||
* If it failed or lost focus, this would return 0.
|
||||
*/
|
||||
uint32_t GetSelectionStartOffset();
|
||||
|
||||
/**
|
||||
* OnStartOffsetUpdatedInChild() is called when composition start offset
|
||||
* is updated in the child process. I.e., this is called and never called
|
||||
* if the composition is in this process.
|
||||
* @param aStartOffset New composition start offset with native
|
||||
* linebreaks.
|
||||
*/
|
||||
void OnStartOffsetUpdatedInChild(uint32_t aStartOffset);
|
||||
|
||||
/**
|
||||
* CompositionEventDispatcher dispatches the specified composition (or text)
|
||||
* event.
|
||||
|
|
|
@ -797,6 +797,10 @@ void HTMLMediaElement::AbortExistingLoads()
|
|||
AddRemoveSelfReference();
|
||||
|
||||
mIsRunningSelectResource = false;
|
||||
|
||||
if (mTextTrackManager) {
|
||||
mTextTrackManager->NotifyReset();
|
||||
}
|
||||
}
|
||||
|
||||
void HTMLMediaElement::NoSupportedMediaSourceError()
|
||||
|
|
|
@ -527,6 +527,11 @@ TextTrackManager::TimeMarchesOn()
|
|||
|
||||
mTimeMarchesOnDispatched = false;
|
||||
|
||||
// Early return if we don't have any TextTracks.
|
||||
if (mTextTracks->Length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsISupports* parentObject =
|
||||
mMediaElement->OwnerDoc()->GetParentObject();
|
||||
if (NS_WARN_IF(!parentObject)) {
|
||||
|
@ -564,6 +569,11 @@ TextTrackManager::TimeMarchesOn()
|
|||
}
|
||||
// Populate otherCues with 'non-active" cues.
|
||||
if (hasNormalPlayback) {
|
||||
if (currentPlaybackTime < mLastTimeMarchesOnCalled) {
|
||||
// TODO: Add log and find the root cause why the
|
||||
// playback position goes backward.
|
||||
mLastTimeMarchesOnCalled = currentPlaybackTime;
|
||||
}
|
||||
media::Interval<double> interval(mLastTimeMarchesOnCalled,
|
||||
currentPlaybackTime);
|
||||
otherCues = mNewCues->GetCueListByTimeInterval(interval);;
|
||||
|
@ -718,5 +728,11 @@ TextTrackManager::NotifyCueUpdated(TextTrackCue *aCue)
|
|||
DispatchTimeMarchesOn();
|
||||
}
|
||||
|
||||
void
|
||||
TextTrackManager::NotifyReset()
|
||||
{
|
||||
mLastTimeMarchesOnCalled = 0.0;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -100,6 +100,8 @@ public:
|
|||
|
||||
void NotifyCueUpdated(TextTrackCue *aCue);
|
||||
|
||||
void NotifyReset();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Converts the TextTrackCue's cuetext into a tree of DOM objects
|
||||
|
|
|
@ -1085,6 +1085,23 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
const unsigned long QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK = 0x0000;
|
||||
const unsigned long QUERY_CONTENT_FLAG_USE_XP_LINE_BREAK = 0x0001;
|
||||
|
||||
/**
|
||||
* sendQueryContentEvent()'s aAdditionalFlags may have one of following
|
||||
* flags when aType is QUERY_SELECTED_TEXT. If one of them is set,
|
||||
* the result is the first range of the selection type. See also
|
||||
* nsISelectionController::SELECTION_*.
|
||||
*/
|
||||
const unsigned long QUERY_CONTENT_FLAG_SELECTION_SPELLCHECK = 0x0002;
|
||||
const unsigned long QUERY_CONTENT_FLAG_SELECTION_IME_RAWINPUT = 0x0004;
|
||||
const unsigned long QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDRAWTEXT = 0x0008;
|
||||
const unsigned long QUERY_CONTENT_FLAG_SELECTION_IME_CONVERTEDTEXT = 0x0010;
|
||||
const unsigned long QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDCONVERTEDTEXT =
|
||||
0x0020;
|
||||
const unsigned long QUERY_CONTENT_FLAG_SELECTION_ACCESSIBILITY = 0x0040;
|
||||
const unsigned long QUERY_CONTENT_FLAG_SELECTION_FIND = 0x0080;
|
||||
const unsigned long QUERY_CONTENT_FLAG_SELECTION_URLSECONDARY = 0x0100;
|
||||
const unsigned long QUERY_CONTENT_FLAG_SELECTION_URLSTRIKEOUT = 0x0200;
|
||||
|
||||
/**
|
||||
* Synthesize a query content event. Note that the result value returned here
|
||||
* is in LayoutDevice pixels rather than CSS pixels.
|
||||
|
|
|
@ -1866,7 +1866,7 @@ TabParent::RecvNotifyIMEFocus(const ContentCache& aContentCache,
|
|||
return true;
|
||||
}
|
||||
|
||||
mContentCache.AssignContent(aContentCache, &aIMENotification);
|
||||
mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
|
||||
IMEStateManager::NotifyIME(aIMENotification, widget, true);
|
||||
|
||||
if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
|
||||
|
@ -1892,7 +1892,7 @@ TabParent::RecvNotifyIMETextChange(const ContentCache& aContentCache,
|
|||
"The widget doesn't want text change notification caused by composition");
|
||||
#endif
|
||||
|
||||
mContentCache.AssignContent(aContentCache, &aIMENotification);
|
||||
mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
|
||||
mContentCache.MaybeNotifyIME(widget, aIMENotification);
|
||||
return true;
|
||||
}
|
||||
|
@ -1907,7 +1907,7 @@ TabParent::RecvNotifyIMECompositionUpdate(
|
|||
return true;
|
||||
}
|
||||
|
||||
mContentCache.AssignContent(aContentCache, &aIMENotification);
|
||||
mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
|
||||
mContentCache.MaybeNotifyIME(widget, aIMENotification);
|
||||
return true;
|
||||
}
|
||||
|
@ -1920,7 +1920,7 @@ TabParent::RecvNotifyIMESelection(const ContentCache& aContentCache,
|
|||
if (!widget)
|
||||
return true;
|
||||
|
||||
mContentCache.AssignContent(aContentCache, &aIMENotification);
|
||||
mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
|
||||
mContentCache.MaybeNotifyIME(widget, aIMENotification);
|
||||
return true;
|
||||
}
|
||||
|
@ -1933,7 +1933,7 @@ TabParent::RecvUpdateContentCache(const ContentCache& aContentCache)
|
|||
return true;
|
||||
}
|
||||
|
||||
mContentCache.AssignContent(aContentCache);
|
||||
mContentCache.AssignContent(aContentCache, widget);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1962,7 +1962,7 @@ TabParent::RecvNotifyIMEPositionChange(const ContentCache& aContentCache,
|
|||
return true;
|
||||
}
|
||||
|
||||
mContentCache.AssignContent(aContentCache, &aIMENotification);
|
||||
mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
|
||||
mContentCache.MaybeNotifyIME(widget, aIMENotification);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -40,8 +40,10 @@ using CrashReporter::GetIDFromMinidump;
|
|||
#include "WMFDecoderModule.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_EME
|
||||
#include "mozilla/dom/WidevineCDMManifestBinding.h"
|
||||
#include "widevine-adapter/WidevineAdapter.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -807,6 +809,7 @@ GMPParent::ReadGMPMetaData()
|
|||
return ReadGMPInfoFile(infoFile);
|
||||
}
|
||||
|
||||
#ifdef MOZ_EME
|
||||
// Maybe this is the Widevine adapted plugin?
|
||||
nsCOMPtr<nsIFile> manifestFile;
|
||||
rv = mDirectory->Clone(getter_AddRefs(manifestFile));
|
||||
|
@ -815,6 +818,9 @@ GMPParent::ReadGMPMetaData()
|
|||
}
|
||||
manifestFile->AppendRelativePath(NS_LITERAL_STRING("manifest.json"));
|
||||
return ReadChromiumManifestFile(manifestFile);
|
||||
#else
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
#endif
|
||||
}
|
||||
|
||||
RefPtr<GenericPromise>
|
||||
|
@ -928,6 +934,7 @@ GMPParent::ParseChromiumManifest(nsString aJSON)
|
|||
LOGD("%s: for '%s'", __FUNCTION__, NS_LossyConvertUTF16toASCII(aJSON).get());
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
#ifdef MOZ_EME
|
||||
mozilla::dom::WidevineCDMManifest m;
|
||||
if (!m.Init(aJSON)) {
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
|
@ -960,6 +967,10 @@ GMPParent::ParseChromiumManifest(nsString aJSON)
|
|||
#endif
|
||||
|
||||
return GenericPromise::CreateAndResolve(true, __func__);
|
||||
#else
|
||||
MOZ_ASSERT_UNREACHABLE("don't call me if EME isn't enabled");
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -236,4 +236,19 @@ WMFMediaDataDecoder::ProcessConfigurationChanged(UniquePtr<TrackInfo>&& aConfig)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
WMFMediaDataDecoder::SetSeekThreshold(const media::TimeUnit& aTime)
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown);
|
||||
|
||||
RefPtr<WMFMediaDataDecoder> self = this;
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableFunction([self, aTime]() {
|
||||
media::TimeUnit threshold = aTime;
|
||||
self->mMFTManager->SetSeekThreshold(threshold);
|
||||
});
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -37,7 +37,10 @@ public:
|
|||
virtual HRESULT Output(int64_t aStreamOffset,
|
||||
RefPtr<MediaData>& aOutput) = 0;
|
||||
|
||||
void Flush() { mDecoder->Flush(); }
|
||||
void Flush() {
|
||||
mDecoder->Flush();
|
||||
mSeekTargetThreshold.reset();
|
||||
}
|
||||
|
||||
void Drain()
|
||||
{
|
||||
|
@ -57,9 +60,15 @@ public:
|
|||
|
||||
virtual const char* GetDescriptionName() const = 0;
|
||||
|
||||
virtual void SetSeekThreshold(const media::TimeUnit& aTime) {
|
||||
mSeekTargetThreshold = Some(aTime);
|
||||
}
|
||||
|
||||
protected:
|
||||
// IMFTransform wrapper that performs the decoding.
|
||||
RefPtr<MFTDecoder> mDecoder;
|
||||
|
||||
Maybe<media::TimeUnit> mSeekTargetThreshold;
|
||||
};
|
||||
|
||||
// Decodes audio and video using Windows Media Foundation. Samples are decoded
|
||||
|
@ -93,6 +102,8 @@ public:
|
|||
return mMFTManager ? mMFTManager->GetDescriptionName() : "";
|
||||
}
|
||||
|
||||
virtual void SetSeekThreshold(const media::TimeUnit& aTime) override;
|
||||
|
||||
private:
|
||||
|
||||
// Called on the task queue. Inserts the sample into the decoder, and
|
||||
|
|
|
@ -789,6 +789,20 @@ WMFVideoMFTManager::Output(int64_t aStreamOffset,
|
|||
}
|
||||
continue;
|
||||
}
|
||||
if (mSeekTargetThreshold.isSome()) {
|
||||
media::TimeUnit pts = GetSampleTime(sample);
|
||||
if (!pts.IsValid()) {
|
||||
return E_FAIL;
|
||||
}
|
||||
if (pts < mSeekTargetThreshold.ref()) {
|
||||
LOG("Dropping video frame which pts is smaller than seek target.");
|
||||
// It is necessary to clear the pointer to release the previous output
|
||||
// buffer.
|
||||
sample = nullptr;
|
||||
continue;
|
||||
}
|
||||
mSeekTargetThreshold.reset();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Else unexpected error, assert, and bail.
|
||||
|
|
|
@ -347,8 +347,10 @@ nsEditorSpellCheck::InitSpellChecker(nsIEditor* aEditor, bool aEnableSelectionCh
|
|||
|
||||
nsCOMPtr<nsISelection> domSelection;
|
||||
aEditor->GetSelection(getter_AddRefs(domSelection));
|
||||
RefPtr<Selection> selection = static_cast<Selection*>(domSelection.get());
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
|
||||
if (NS_WARN_IF(!domSelection)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
RefPtr<Selection> selection = domSelection->AsSelection();
|
||||
|
||||
int32_t count = 0;
|
||||
|
||||
|
|
|
@ -83,7 +83,8 @@ NS_IMETHODIMP TypeInState::NotifySelectionChanged(nsIDOMDocument *, nsISelection
|
|||
// XXX:
|
||||
// XXX: This code temporarily fixes the problem where clicking the mouse in
|
||||
// XXX: the same location clears the type-in-state.
|
||||
RefPtr<Selection> selection = static_cast<Selection*>(aSelection);
|
||||
RefPtr<Selection> selection =
|
||||
aSelection ? aSelection->AsSelection() : nullptr;
|
||||
|
||||
if (aSelection) {
|
||||
int32_t rangeCount = selection->RangeCount();
|
||||
|
|
|
@ -661,11 +661,11 @@ nsEditor::GetSelection(SelectionType aSelectionType)
|
|||
{
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
nsresult res = GetSelection(aSelectionType, getter_AddRefs(sel));
|
||||
if (NS_FAILED(res)) {
|
||||
if (NS_WARN_IF(NS_FAILED(res)) || NS_WARN_IF(!sel)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return static_cast<Selection*>(sel.get());
|
||||
return sel->AsSelection();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -8168,7 +8168,10 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection* aSelection)
|
|||
if (!mListenerEnabled) {
|
||||
return NS_OK;
|
||||
}
|
||||
RefPtr<Selection> selection = static_cast<Selection*>(aSelection);
|
||||
if (NS_WARN_IF(!aSelection)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
RefPtr<Selection> selection = aSelection->AsSelection();
|
||||
// get the (collapsed) selection location
|
||||
nsCOMPtr<nsIDOMNode> selNode;
|
||||
int32_t selOffset;
|
||||
|
|
|
@ -47,7 +47,7 @@ nsTextEditRules::CheckBidiLevelForDeletion(Selection* aSelection,
|
|||
nsBidiLevel levelBefore;
|
||||
nsBidiLevel levelAfter;
|
||||
RefPtr<nsFrameSelection> frameSelection =
|
||||
static_cast<Selection*>(aSelection)->GetFrameSelection();
|
||||
aSelection->AsSelection()->GetFrameSelection();
|
||||
NS_ENSURE_TRUE(frameSelection, NS_ERROR_NULL_POINTER);
|
||||
|
||||
nsPrevNextBidiLevels levels = frameSelection->
|
||||
|
|
|
@ -523,7 +523,7 @@ nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus,
|
|||
return result;
|
||||
}
|
||||
|
||||
RefPtr<Selection> selection = static_cast<Selection*>(domSelection.get());
|
||||
RefPtr<Selection> selection = domSelection->AsSelection();
|
||||
|
||||
bool isCollapsed = selection->IsCollapsed();
|
||||
|
||||
|
@ -2526,7 +2526,7 @@ nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockS
|
|||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_TRUE(domSelection, NS_ERROR_FAILURE);
|
||||
|
||||
RefPtr<Selection> selection = static_cast<Selection*>(domSelection.get());
|
||||
RefPtr<Selection> selection = domSelection->AsSelection();
|
||||
|
||||
// The calling function should have done the GetIsCollapsed()
|
||||
// check already. Just assume it's collapsed!
|
||||
|
@ -2744,7 +2744,7 @@ nsTextServicesDocument::GetUncollapsedSelection(nsITextServicesDocument::TSDBloc
|
|||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_TRUE(domSelection, NS_ERROR_FAILURE);
|
||||
|
||||
RefPtr<Selection> selection = static_cast<Selection*>(domSelection.get());
|
||||
RefPtr<Selection> selection = domSelection->AsSelection();
|
||||
|
||||
// It is assumed that the calling function has made sure that the
|
||||
// selection is not collapsed, and that the input params to this
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<script>
|
||||
|
||||
document.createElement('canvas').getContext('2d').measureText("\u0CC4\u0CA7\u200C");
|
||||
|
||||
</script>
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div style="font-family:Arial Unicode MS">ೄಧ‌</div>
|
||||
</body>
|
||||
</html>
|
|
@ -103,6 +103,8 @@ load 693143-1.html
|
|||
load 696936-1.html
|
||||
load 699563-1.html
|
||||
load 710149-1.html
|
||||
load 766452-1.html
|
||||
load 766452-2.html
|
||||
load 768079-1.html
|
||||
load 783041-1.html
|
||||
load 783041-2.html
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
function f() {
|
||||
x = arguments;
|
||||
delete x[1];
|
||||
}
|
||||
f(0, 1);
|
||||
gc();
|
||||
assertEq(x.length, 2);
|
||||
assertEq(0 in x, true);
|
||||
assertEq(1 in x, false);
|
|
@ -768,7 +768,7 @@ MakeSingletonTypeSetFromKey(CompilerConstraintList* constraints, TypeSet::Object
|
|||
// we want to invalidate and mark this TypeSet as containing AnyObject
|
||||
// (because mutating __proto__ will change an object's ObjectGroup).
|
||||
MOZ_ASSERT(constraints);
|
||||
key->hasStableClassAndProto(constraints);
|
||||
(void)key->hasStableClassAndProto(constraints);
|
||||
|
||||
LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
|
||||
return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::ObjectType(key));
|
||||
|
@ -6040,6 +6040,9 @@ PropertyTypeIncludes(TempAllocator& alloc, HeapTypeSetKey property,
|
|||
types = types->clone(alloc.lifoAlloc());
|
||||
else
|
||||
types = alloc.lifoAlloc()->new_<TemporaryTypeSet>();
|
||||
if (!types) {
|
||||
return false;
|
||||
}
|
||||
types->addType(newType, alloc.lifoAlloc());
|
||||
}
|
||||
|
||||
|
|
|
@ -262,13 +262,13 @@ BitArrayIndexToWordMask(size_t i)
|
|||
}
|
||||
|
||||
static inline bool
|
||||
IsBitArrayElementSet(size_t* array, size_t length, size_t i)
|
||||
IsBitArrayElementSet(const size_t* array, size_t length, size_t i)
|
||||
{
|
||||
return array[BitArrayIndexToWordIndex(length, i)] & BitArrayIndexToWordMask(i);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsAnyBitArrayElementSet(size_t* array, size_t length)
|
||||
IsAnyBitArrayElementSet(const size_t* array, size_t length)
|
||||
{
|
||||
unsigned numWords = NumWordsForBitArrayOfLength(length);
|
||||
for (unsigned i = 0; i < numWords; ++i) {
|
||||
|
|
|
@ -24,7 +24,7 @@ using namespace js::gc;
|
|||
RareArgumentsData::bytesRequired(size_t numActuals)
|
||||
{
|
||||
size_t extraBytes = NumWordsForBitArrayOfLength(numActuals) * sizeof(size_t);
|
||||
return sizeof(RareArgumentsData) + extraBytes;
|
||||
return offsetof(RareArgumentsData, deletedBits_) + extraBytes;
|
||||
}
|
||||
|
||||
/* static */ RareArgumentsData*
|
||||
|
@ -38,9 +38,7 @@ RareArgumentsData::create(JSContext* cx, ArgumentsObject* obj)
|
|||
|
||||
mozilla::PodZero(data, bytes);
|
||||
|
||||
size_t* deletedBits = reinterpret_cast<size_t*>(data + sizeof(RareArgumentsData));
|
||||
|
||||
return new(data) RareArgumentsData(deletedBits);
|
||||
return new(data) RareArgumentsData();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -29,12 +29,9 @@ class RareArgumentsData
|
|||
// Pointer to an array of bits indicating, for every argument in
|
||||
// [0, initialLength) whether the element has been deleted. See
|
||||
// ArgumentsObject::isElementDeleted comment.
|
||||
size_t* deletedBits_;
|
||||
|
||||
explicit RareArgumentsData(size_t* deletedBits)
|
||||
: deletedBits_(deletedBits)
|
||||
{}
|
||||
size_t deletedBits_[1];
|
||||
|
||||
RareArgumentsData() = default;
|
||||
RareArgumentsData(const RareArgumentsData&) = delete;
|
||||
void operator=(const RareArgumentsData&) = delete;
|
||||
|
||||
|
@ -49,7 +46,7 @@ class RareArgumentsData
|
|||
MOZ_ASSERT(i < len);
|
||||
return IsBitArrayElementSet(deletedBits_, len, i);
|
||||
}
|
||||
void markElementDeleted(size_t len, size_t i) const {
|
||||
void markElementDeleted(size_t len, size_t i) {
|
||||
MOZ_ASSERT(i < len);
|
||||
SetBitArrayElement(deletedBits_, len, i);
|
||||
}
|
||||
|
|
|
@ -1080,11 +1080,12 @@ Debugger::wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp)
|
|||
|
||||
if (vp.isObject()) {
|
||||
RootedObject obj(cx, &vp.toObject());
|
||||
Rooted<DebuggerObject*> dobj(cx);
|
||||
|
||||
if (!wrapDebuggeeObject(cx, &obj))
|
||||
if (!wrapDebuggeeObject(cx, obj, &dobj))
|
||||
return false;
|
||||
|
||||
vp.setObject(*obj);
|
||||
vp.setObject(*dobj);
|
||||
} else if (vp.isMagic()) {
|
||||
RootedPlainObject optObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
||||
if (!optObj)
|
||||
|
@ -1117,7 +1118,8 @@ Debugger::wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp)
|
|||
}
|
||||
|
||||
bool
|
||||
Debugger::wrapDebuggeeObject(JSContext* cx, MutableHandleObject obj)
|
||||
Debugger::wrapDebuggeeObject(JSContext* cx, HandleObject obj,
|
||||
MutableHandle<DebuggerObject*> result)
|
||||
{
|
||||
MOZ_ASSERT(obj);
|
||||
|
||||
|
@ -1130,12 +1132,12 @@ Debugger::wrapDebuggeeObject(JSContext* cx, MutableHandleObject obj)
|
|||
|
||||
DependentAddPtr<ObjectWeakMap> p(cx, objects, obj);
|
||||
if (p) {
|
||||
obj.set(p->value());
|
||||
result.set(&p->value()->as<DebuggerObject>());
|
||||
} else {
|
||||
/* Create a new Debugger.Object for obj. */
|
||||
RootedNativeObject debugger(cx, object);
|
||||
RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject());
|
||||
NativeObject* dobj = DebuggerObject::create(cx, proto, obj, debugger);
|
||||
Rooted<DebuggerObject*> dobj(cx, DebuggerObject::create(cx, proto, obj, debugger));
|
||||
if (!dobj)
|
||||
return false;
|
||||
|
||||
|
@ -1154,7 +1156,7 @@ Debugger::wrapDebuggeeObject(JSContext* cx, MutableHandleObject obj)
|
|||
}
|
||||
}
|
||||
|
||||
obj.set(dobj);
|
||||
result.set(dobj);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -7906,7 +7908,7 @@ DebuggerObject::protoGetter(JSContext* cx, unsigned argc, Value* vp)
|
|||
{
|
||||
THIS_DEBUGOBJECT(cx, argc, vp, "get proto", args, object)
|
||||
|
||||
RootedObject result(cx);
|
||||
Rooted<DebuggerObject*> result(cx);
|
||||
if (!DebuggerObject::getPrototypeOf(cx, object, &result))
|
||||
return false;
|
||||
|
||||
|
@ -8076,11 +8078,11 @@ DebuggerObject::boundTargetFunctionGetter(JSContext* cx, unsigned argc, Value* v
|
|||
return true;
|
||||
}
|
||||
|
||||
RootedObject result(cx);
|
||||
Rooted<DebuggerObject*> result(cx);
|
||||
if (!DebuggerObject::boundTargetFunction(cx, object, &result))
|
||||
return false;
|
||||
|
||||
args.rval().setObjectOrNull(result);
|
||||
args.rval().setObject(*result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -8128,7 +8130,7 @@ DebuggerObject::globalGetter(JSContext* cx, unsigned argc, Value* vp)
|
|||
{
|
||||
THIS_DEBUGOBJECT(cx, argc, vp, "get global", args, object)
|
||||
|
||||
RootedObject result(cx);
|
||||
Rooted<DebuggerObject*> result(cx);
|
||||
if (!DebuggerObject::global(cx, object, &result))
|
||||
return false;
|
||||
|
||||
|
@ -8882,13 +8884,14 @@ DebuggerObject::className(JSContext* cx, Handle<DebuggerObject*> object,
|
|||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerObject::global(JSContext* cx, Handle<DebuggerObject*> object, MutableHandleObject result)
|
||||
DebuggerObject::global(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandle<DebuggerObject*> result)
|
||||
{
|
||||
RootedObject referent(cx, object->referent());
|
||||
Debugger* dbg = object->owner();
|
||||
|
||||
result.set(&referent->global());
|
||||
return dbg->wrapDebuggeeObject(cx, result);
|
||||
RootedObject global(cx, &referent->global());
|
||||
return dbg->wrapDebuggeeObject(cx, global, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -8951,15 +8954,15 @@ DebuggerObject::parameterNames(JSContext* cx, Handle<DebuggerObject*> object,
|
|||
|
||||
/* static */ bool
|
||||
DebuggerObject::boundTargetFunction(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandleObject result)
|
||||
MutableHandle<DebuggerObject*> result)
|
||||
{
|
||||
MOZ_ASSERT(isBoundFunction(cx, object));
|
||||
|
||||
RootedFunction referent(cx, &object->referent()->as<JSFunction>());
|
||||
Debugger* dbg = object->owner();
|
||||
|
||||
result.set(referent->getBoundFunctionTarget());
|
||||
return dbg->wrapDebuggeeObject(cx, result);
|
||||
RootedObject target(cx, referent->getBoundFunctionTarget());
|
||||
return dbg->wrapDebuggeeObject(cx, target, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -9093,7 +9096,7 @@ DebuggerObject::isFrozen(JSContext* cx, Handle<DebuggerObject*> object, bool& re
|
|||
|
||||
/* static */ bool
|
||||
DebuggerObject::getPrototypeOf(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandleObject result)
|
||||
MutableHandle<DebuggerObject*> result)
|
||||
{
|
||||
RootedObject referent(cx, object->referent());
|
||||
Debugger* dbg = object->owner();
|
||||
|
@ -9105,8 +9108,12 @@ DebuggerObject::getPrototypeOf(JSContext* cx, Handle<DebuggerObject*> object,
|
|||
return false;
|
||||
}
|
||||
|
||||
result.set(proto);
|
||||
return !result || dbg->wrapDebuggeeObject(cx, result);
|
||||
if (!proto) {
|
||||
result.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return dbg->wrapDebuggeeObject(cx, proto, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -9480,11 +9487,7 @@ DebuggerObject::unwrap(JSContext* cx, Handle<DebuggerObject*> object,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!dbg->wrapDebuggeeObject(cx, &unwrapped))
|
||||
return false;
|
||||
|
||||
result.set(&unwrapped->as<DebuggerObject>());
|
||||
return true;
|
||||
return dbg->wrapDebuggeeObject(cx, unwrapped, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
|
|
@ -245,6 +245,8 @@ typedef mozilla::Variant<JSScript*, WasmInstanceObject*> DebuggerScriptReferent;
|
|||
// denoting the synthesized source of a wasm module.
|
||||
typedef mozilla::Variant<ScriptSourceObject*, WasmInstanceObject*> DebuggerSourceReferent;
|
||||
|
||||
class DebuggerObject;
|
||||
|
||||
class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
{
|
||||
friend class Breakpoint;
|
||||
|
@ -928,7 +930,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
* form { uninitialized: true }.
|
||||
*/
|
||||
MOZ_MUST_USE bool wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
|
||||
MOZ_MUST_USE bool wrapDebuggeeObject(JSContext* cx, MutableHandleObject obj);
|
||||
MOZ_MUST_USE bool wrapDebuggeeObject(JSContext* cx, HandleObject obj,
|
||||
MutableHandle<DebuggerObject*> result);
|
||||
|
||||
/*
|
||||
* Unwrap a Debug.Object, without rewrapping it for any particular debuggee
|
||||
|
@ -1072,7 +1075,7 @@ class DebuggerObject : public NativeObject
|
|||
static MOZ_MUST_USE bool className(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandleString result);
|
||||
static MOZ_MUST_USE bool global(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandleObject result);
|
||||
MutableHandle<DebuggerObject*> result);
|
||||
static MOZ_MUST_USE bool name(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandleString result);
|
||||
static MOZ_MUST_USE bool displayName(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
|
@ -1080,7 +1083,7 @@ class DebuggerObject : public NativeObject
|
|||
static MOZ_MUST_USE bool parameterNames(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandle<StringVector> result);
|
||||
static MOZ_MUST_USE bool boundTargetFunction(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandleObject result);
|
||||
MutableHandle<DebuggerObject*> result);
|
||||
static MOZ_MUST_USE bool boundThis(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandleValue result);
|
||||
static MOZ_MUST_USE bool boundArguments(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
|
@ -1095,7 +1098,7 @@ class DebuggerObject : public NativeObject
|
|||
static MOZ_MUST_USE bool isSealed(JSContext* cx, Handle<DebuggerObject*> object, bool& result);
|
||||
static MOZ_MUST_USE bool isFrozen(JSContext* cx, Handle<DebuggerObject*> object, bool& result);
|
||||
static MOZ_MUST_USE bool getPrototypeOf(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandleObject result);
|
||||
MutableHandle<DebuggerObject*> result);
|
||||
static MOZ_MUST_USE bool getOwnPropertyNames(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
MutableHandle<IdVector> result);
|
||||
static MOZ_MUST_USE bool getOwnPropertySymbols(JSContext* cx, Handle<DebuggerObject*> object,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
function boom()
|
||||
{
|
||||
var div = document.getElementsByTagName("div")[0];
|
||||
div.childNodes[1].convertPointFromNode({x:0, y:0}, div.childNodes[0]);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();">
|
||||
<div><span>אC</span> </div>
|
||||
</body>
|
||||
</html>
|
|
@ -459,6 +459,7 @@ load 936988-1.html
|
|||
load 942690.html
|
||||
load 973390-1.html
|
||||
load 1001237.html
|
||||
load 1009036.html
|
||||
load 1043163-1.html
|
||||
load 1061028.html
|
||||
load 1107508-1.html
|
||||
|
|
|
@ -417,8 +417,8 @@ nsCaret::GetFrameAndOffset(Selection* aSelection,
|
|||
nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect)
|
||||
{
|
||||
int32_t frameOffset;
|
||||
nsIFrame* frame = GetFrameAndOffset(
|
||||
static_cast<Selection*>(aSelection), nullptr, 0, &frameOffset);
|
||||
Selection* selection = aSelection ? aSelection->AsSelection() : nullptr;
|
||||
nsIFrame* frame = GetFrameAndOffset(selection, nullptr, 0, &frameOffset);
|
||||
if (frame) {
|
||||
*aRect = GetGeometryForFrame(frame, frameOffset, nullptr);
|
||||
}
|
||||
|
@ -428,7 +428,8 @@ nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect)
|
|||
Selection*
|
||||
nsCaret::GetSelectionInternal()
|
||||
{
|
||||
return static_cast<Selection*>(GetSelection());
|
||||
nsISelection* domSelection = GetSelection();
|
||||
return domSelection ? domSelection->AsSelection() : nullptr;
|
||||
}
|
||||
|
||||
void nsCaret::SchedulePaint()
|
||||
|
|
|
@ -835,6 +835,24 @@ public:
|
|||
virtual mozilla::dom::Selection*
|
||||
GetCurrentSelection(mozilla::SelectionType aSelectionType) = 0;
|
||||
|
||||
/**
|
||||
* Gets a selection controller for the focused content in the DOM window
|
||||
* for mDocument.
|
||||
*
|
||||
* @param aFocusedContent If there is focused content in the DOM window,
|
||||
* the focused content will be returned. This may
|
||||
* be nullptr if it's not necessary.
|
||||
* @return A selection controller for focused content.
|
||||
* E.g., if an <input> element has focus, returns
|
||||
* the independent selection controller of it.
|
||||
* If the DOM window does not have focused content
|
||||
* (similar to Document.activeElement), returns
|
||||
* nullptr.
|
||||
*/
|
||||
virtual already_AddRefed<nsISelectionController>
|
||||
GetSelectionControllerForFocusedContent(
|
||||
nsIContent** aFocusedContent = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* Interface to dispatch events via the presshell
|
||||
* @note The caller must have a strong reference to the PresShell.
|
||||
|
|
|
@ -1556,6 +1556,37 @@ PresShell::GetCurrentSelection(SelectionType aSelectionType)
|
|||
return mSelection->GetSelection(aSelectionType);
|
||||
}
|
||||
|
||||
already_AddRefed<nsISelectionController>
|
||||
PresShell::GetSelectionControllerForFocusedContent(nsIContent** aFocusedContent)
|
||||
{
|
||||
if (aFocusedContent) {
|
||||
*aFocusedContent = nullptr;
|
||||
}
|
||||
|
||||
if (mDocument) {
|
||||
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
|
||||
nsCOMPtr<nsIContent> focusedContent =
|
||||
nsFocusManager::GetFocusedDescendant(mDocument->GetWindow(), false,
|
||||
getter_AddRefs(focusedWindow));
|
||||
if (focusedContent) {
|
||||
nsIFrame* frame = focusedContent->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
nsCOMPtr<nsISelectionController> selectionController;
|
||||
frame->GetSelectionController(mPresContext,
|
||||
getter_AddRefs(selectionController));
|
||||
if (selectionController) {
|
||||
if (aFocusedContent) {
|
||||
focusedContent.forget(aFocusedContent);
|
||||
}
|
||||
return selectionController.forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsISelectionController> self(this);
|
||||
return self.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresShell::ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
|
||||
SelectionRegion aRegion,
|
||||
|
@ -5221,7 +5252,7 @@ PresShell::PaintRangePaintInfo(const nsTArray<UniquePtr<RangePaintInfo>>& aItems
|
|||
// selection.
|
||||
RefPtr<nsFrameSelection> frameSelection;
|
||||
if (aSelection) {
|
||||
frameSelection = static_cast<Selection*>(aSelection)->GetFrameSelection();
|
||||
frameSelection = aSelection->AsSelection()->GetFrameSelection();
|
||||
}
|
||||
else {
|
||||
frameSelection = FrameSelection();
|
||||
|
|
|
@ -112,6 +112,9 @@ public:
|
|||
nsISelection** aSelection) override;
|
||||
virtual mozilla::dom::Selection*
|
||||
GetCurrentSelection(SelectionType aSelectionType) override;
|
||||
virtual already_AddRefed<nsISelectionController>
|
||||
GetSelectionControllerForFocusedContent(
|
||||
nsIContent** aFocusedContent = nullptr) override;
|
||||
|
||||
NS_IMETHOD SetDisplaySelection(int16_t aToggle) override;
|
||||
NS_IMETHOD GetDisplaySelection(int16_t *aToggle) override;
|
||||
|
|
|
@ -1039,7 +1039,7 @@ nsTextControlFrame::GetSelectionRange(int32_t* aSelectionStart,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
|
||||
|
||||
dom::Selection* sel = static_cast<dom::Selection*>(selection.get());
|
||||
dom::Selection* sel = selection->AsSelection();
|
||||
if (aDirection) {
|
||||
nsDirection direction = sel->GetSelectionDirection();
|
||||
if (direction == eDirNext) {
|
||||
|
|
|
@ -65,6 +65,8 @@ public:
|
|||
NS_DECL_NSISELECTION
|
||||
NS_DECL_NSISELECTIONPRIVATE
|
||||
|
||||
virtual Selection* AsSelection() override { return this; }
|
||||
|
||||
nsresult EndBatchChangesInternal(int16_t aReason = nsISelectionListener::NO_REASON);
|
||||
|
||||
nsIDocument* GetParentObject() const;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body onload="document.documentElement.offsetHeight; document.getElementById('x').style.position = 'relative';">
|
||||
<div style="-moz-column-width: 200px; -moz-column-fill: auto; height: 200px;">
|
||||
<div style="height: 150px;"></div>
|
||||
<div style="float: left; height: 150px; width: 200px;"></div>
|
||||
<div>
|
||||
<div id="x" style="float: left; height: 150px; width: 200px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,34 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<script>
|
||||
function aC(r, n) { if (r) { r.appendChild(n); } else { rM(n); } }
|
||||
function iB(r, n) { if (r) { r.parentNode.insertBefore(n, r); } else { rM(n); } }
|
||||
allNodes = [];
|
||||
allNodes[0] = document.documentElement;
|
||||
allNodes[50] = document.createTextNode("Foo");
|
||||
allNodes[63] = document.createElementNS("http://www.w3.org/1999/xhtml", "tr");
|
||||
allNodes[73] = document.createElementNS("http://www.w3.org/1999/xhtml", "select");
|
||||
allNodes[76] = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
|
||||
(allNodes[73] || allNodes[72] || allNodes[63] || allNodes[44] || allNodes[5] || document.documentElement).appendChild(allNodes[76]);
|
||||
allNodes[78] = document.createTextNode("\n ");
|
||||
(allNodes[63] || allNodes[44] || allNodes[5] || document.documentElement).appendChild(allNodes[78]);
|
||||
allNodes[88] = document.createElementNS("http://www.w3.org/1999/xhtml", "legend");
|
||||
allNodes[89] = document.createTextNode("Your name");
|
||||
allNodes[98] = document.createTextNode("\n ");
|
||||
allNodes[125] = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
|
||||
allNodes[0].style.MozColumnCount = "115";
|
||||
aC(allNodes[88], allNodes[98]);
|
||||
iB(allNodes[98], allNodes[63]);
|
||||
allNodes[63].style.cssFloat = "right";
|
||||
aC(allNodes[0], allNodes[88]);
|
||||
aC(allNodes[88], allNodes[125]);
|
||||
iB(allNodes[88], allNodes[73]);
|
||||
function run() {
|
||||
iB(allNodes[78], allNodes[89]);
|
||||
aC(allNodes[76], allNodes[50]);
|
||||
}
|
||||
document.body.offsetHeight;
|
||||
setTimeout(run, 0);
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
>><test id=test1>><cr id=test2>>><foo2 id=test3>>>><bdi id=test4>x qJ9_:}6nzX&
|
||||
>>>>><script>
|
||||
function forceGC() {SpecialPowers.forceGC(); }
|
||||
var docElement = document.documentElement;
|
||||
document.addEventListener("DOMContentLoaded", CFcrash, false);
|
||||
function CFcrash() {
|
||||
try { test5 = document.createTextNode("/}F9*D f e /e=*: M[3 b-m#iA& Kj[ ZA- RSOh$-@ *xTk8r_ X:du[Ok 4d;bf|xtS x]sA&"); } catch(e) {}
|
||||
setTimeout('document.execCommand("SelectAll");document.execCommand("InsertText", false, "hello");', 200);
|
||||
setTimeout('test3.parentNode.removeChild(test3); forceGC();', 100);
|
||||
try { document.adoptNode(test4); } catch(e) {}
|
||||
try { test4.appendChild(test5); } catch(e) {}
|
||||
try { test4.setAttribute("dir", "&locale.dir;"); } catch(e) {}
|
||||
try { test1.appendChild(test4); } catch(e) {}
|
||||
try { test4.replaceChild(test2, test4.firstChild); } catch(e) { }
|
||||
}</script>>
|
|
@ -482,6 +482,8 @@ load 737313-3.html
|
|||
test-pref(font.size.inflation.emPerLine,15) load 740199-1.xhtml
|
||||
load 747688.html
|
||||
load 750066.html
|
||||
load 757413.xhtml
|
||||
load 757413-2.html
|
||||
load 762764-1.html
|
||||
load 762902.html
|
||||
load 765409.html
|
||||
|
@ -542,6 +544,7 @@ load 863935.html
|
|||
load 866547-1.html
|
||||
needs-focus pref(accessibility.browsewithcaret,true) load 868906.html
|
||||
asserts(0-5) load 876074-1.html # bug 876749
|
||||
load 876155.html
|
||||
load 885009-1.html
|
||||
load 893496-1.html
|
||||
load 893523.html
|
||||
|
|
|
@ -6555,9 +6555,7 @@ NS_IMETHODIMP
|
|||
SelectionChangeListener::NotifySelectionChanged(nsIDOMDocument* aDoc,
|
||||
nsISelection* aSel, int16_t aReason)
|
||||
{
|
||||
// This cast is valid as nsISelection is a builtinclass which is only
|
||||
// implemented by Selection.
|
||||
RefPtr<Selection> sel = static_cast<Selection*>(aSel);
|
||||
RefPtr<Selection> sel = aSel->AsSelection();
|
||||
|
||||
// Check if the ranges have actually changed
|
||||
// Don't bother checking this if we are hiding changes.
|
||||
|
|
|
@ -5,3 +5,4 @@ task:
|
|||
payload:
|
||||
command:
|
||||
- --download-symbols=true
|
||||
maxRunTime: 5400
|
||||
|
|
|
@ -5,3 +5,4 @@ task:
|
|||
payload:
|
||||
command:
|
||||
- --download-symbols=true
|
||||
maxRunTime: 5400
|
||||
|
|
|
@ -2481,23 +2481,14 @@ GeckoDriver.prototype.getWindowSize = function(cmd, resp) {
|
|||
* Not supported on B2G. The supplied width and height values refer to
|
||||
* the window outerWidth and outerHeight values, which include scroll
|
||||
* bars, title bars, etc.
|
||||
*
|
||||
* An error will be returned if the requested window size would result
|
||||
* in the window being in the maximized state.
|
||||
*/
|
||||
GeckoDriver.prototype.setWindowSize = function(cmd, resp) {
|
||||
if (this.appName != "Firefox") {
|
||||
throw new UnsupportedOperationError();
|
||||
}
|
||||
|
||||
let width = parseInt(cmd.parameters.width);
|
||||
let height = parseInt(cmd.parameters.height);
|
||||
|
||||
let {width, height} = cmd.parameters;
|
||||
let win = this.getCurrentWindow();
|
||||
if (width >= win.screen.availWidth || height >= win.screen.availHeight) {
|
||||
throw new UnsupportedOperationError("Requested size exceeds screen size")
|
||||
}
|
||||
|
||||
win.resizeTo(width, height);
|
||||
};
|
||||
|
||||
|
|
|
@ -43,17 +43,13 @@ class TestSetWindowSize(MarionetteTestCase):
|
|||
self.assertEqual(size['height'], height,
|
||||
"Window height is %s but should be %s" % (size['height'], height))
|
||||
|
||||
def test_that_we_throw_an_error_when_trying_to_set_maximum_size(self):
|
||||
# valid size
|
||||
width = self.max_width - 100
|
||||
height = self.max_height - 100
|
||||
self.marionette.set_window_size(width, height)
|
||||
# invalid size (cannot maximize)
|
||||
with self.assertRaisesRegexp(UnsupportedOperationException, "Requested size exceeds screen size"):
|
||||
self.marionette.set_window_size(self.max_width, self.max_height)
|
||||
def test_possible_to_request_window_larger_than_screen(self):
|
||||
self.marionette.set_window_size(100000, 100000)
|
||||
size = self.marionette.window_size
|
||||
self.assertEqual(size['width'], width, "Window width should not have changed")
|
||||
self.assertEqual(size['height'], height, "Window height should not have changed")
|
||||
|
||||
# In X the window size may be greater than the bounds of the screen
|
||||
self.assertGreaterEqual(size["width"], self.max_width)
|
||||
self.assertGreaterEqual(size["height"], self.max_height)
|
||||
|
||||
def test_that_we_can_maximise_the_window(self):
|
||||
# valid size
|
||||
|
|
|
@ -1558,6 +1558,17 @@ function synthesizeCompositionChange(aEvent, aWindow = window, aCallback)
|
|||
const QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK = 0x0000;
|
||||
const QUERY_CONTENT_FLAG_USE_XP_LINE_BREAK = 0x0001;
|
||||
|
||||
const QUERY_CONTENT_FLAG_SELECTION_NORMAL = 0x0000;
|
||||
const QUERY_CONTENT_FLAG_SELECTION_SPELLCHECK = 0x0002;
|
||||
const QUERY_CONTENT_FLAG_SELECTION_IME_RAWINPUT = 0x0004;
|
||||
const QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDRAWTEXT = 0x0008;
|
||||
const QUERY_CONTENT_FLAG_SELECTION_IME_CONVERTEDTEXT = 0x0010;
|
||||
const QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDCONVERTEDTEXT = 0x0020;
|
||||
const QUERY_CONTENT_FLAG_SELECTION_ACCESSIBILITY = 0x0040;
|
||||
const QUERY_CONTENT_FLAG_SELECTION_FIND = 0x0080;
|
||||
const QUERY_CONTENT_FLAG_SELECTION_URLSECONDARY = 0x0100;
|
||||
const QUERY_CONTENT_FLAG_SELECTION_URLSTRIKEOUT = 0x0200;
|
||||
|
||||
const SELECTION_SET_FLAG_USE_NATIVE_LINE_BREAK = 0x0000;
|
||||
const SELECTION_SET_FLAG_USE_XP_LINE_BREAK = 0x0001;
|
||||
const SELECTION_SET_FLAG_REVERSE = 0x0002;
|
||||
|
@ -1565,19 +1576,27 @@ const SELECTION_SET_FLAG_REVERSE = 0x0002;
|
|||
/**
|
||||
* Synthesize a query selected text event.
|
||||
*
|
||||
* @param aSelectionType Optional, one of QUERY_CONTENT_FLAG_SELECTION_*.
|
||||
* If null, QUERY_CONTENT_FLAG_SELECTION_NORMAL will
|
||||
* be used.
|
||||
* @param aWindow Optional (If null, current |window| will be used)
|
||||
* @return An nsIQueryContentEventResult object. If this failed,
|
||||
* the result might be null.
|
||||
*/
|
||||
function synthesizeQuerySelectedText(aWindow)
|
||||
function synthesizeQuerySelectedText(aSelectionType, aWindow)
|
||||
{
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
if (!utils) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var flags = QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK;
|
||||
if (aSelectionType) {
|
||||
flags |= aSelectionType;
|
||||
}
|
||||
|
||||
return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0,
|
||||
QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK);
|
||||
flags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -72,13 +72,11 @@ public:
|
|||
* mozilla::ContentCache
|
||||
*****************************************************************************/
|
||||
|
||||
PRLogModuleInfo* sContentCacheLog = nullptr;
|
||||
LazyLogModule sContentCacheLog("ContentCacheWidgets");
|
||||
|
||||
ContentCache::ContentCache()
|
||||
: mCompositionStart(UINT32_MAX)
|
||||
{
|
||||
if (!sContentCacheLog) {
|
||||
sContentCacheLog = PR_NewLogModule("ContentCacheWidgets");
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
@ -96,6 +94,7 @@ ContentCacheInChild::Clear()
|
|||
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
||||
("ContentCacheInChild: 0x%p Clear()", this));
|
||||
|
||||
mCompositionStart = UINT32_MAX;
|
||||
mText.Truncate();
|
||||
mSelection.Clear();
|
||||
mFirstCharRect.SetEmpty();
|
||||
|
@ -286,6 +285,7 @@ ContentCacheInChild::CacheTextRects(nsIWidget* aWidget,
|
|||
this, aWidget, GetNotificationName(aNotification), mCaret.mOffset,
|
||||
GetBoolName(mCaret.IsValid())));
|
||||
|
||||
mCompositionStart = UINT32_MAX;
|
||||
mTextRectArray.Clear();
|
||||
mSelection.mAnchorCharRect.SetEmpty();
|
||||
mSelection.mFocusCharRect.SetEmpty();
|
||||
|
@ -300,12 +300,15 @@ ContentCacheInChild::CacheTextRects(nsIWidget* aWidget,
|
|||
RefPtr<TextComposition> textComposition =
|
||||
IMEStateManager::GetTextCompositionFor(aWidget);
|
||||
if (textComposition) {
|
||||
// mCompositionStart may be updated by some composition event handlers.
|
||||
// So, let's update it with the latest information.
|
||||
mCompositionStart = textComposition->NativeOffsetOfStartComposition();
|
||||
// Note that TextComposition::String() may not be modified here because
|
||||
// it's modified after all edit action listeners are performed but this
|
||||
// is called while some of them are performed.
|
||||
uint32_t length = textComposition->LastData().Length();
|
||||
mTextRectArray.mRects.SetCapacity(length);
|
||||
mTextRectArray.mStart = textComposition->NativeOffsetOfStartComposition();
|
||||
mTextRectArray.mStart = mCompositionStart;
|
||||
uint32_t endOffset = mTextRectArray.mStart + length;
|
||||
for (uint32_t i = mTextRectArray.mStart; i < endOffset; i++) {
|
||||
LayoutDeviceIntRect charRect;
|
||||
|
@ -428,7 +431,6 @@ ContentCacheInChild::SetSelection(nsIWidget* aWidget,
|
|||
ContentCacheInParent::ContentCacheInParent()
|
||||
: ContentCache()
|
||||
, mCommitStringByRequest(nullptr)
|
||||
, mCompositionStart(UINT32_MAX)
|
||||
, mPendingEventsNeedingAck(0)
|
||||
, mIsComposing(false)
|
||||
{
|
||||
|
@ -436,8 +438,10 @@ ContentCacheInParent::ContentCacheInParent()
|
|||
|
||||
void
|
||||
ContentCacheInParent::AssignContent(const ContentCache& aOther,
|
||||
nsIWidget* aWidget,
|
||||
const IMENotification* aNotification)
|
||||
{
|
||||
mCompositionStart = aOther.mCompositionStart;
|
||||
mText = aOther.mText;
|
||||
mSelection = aOther.mSelection;
|
||||
mFirstCharRect = aOther.mFirstCharRect;
|
||||
|
@ -445,12 +449,20 @@ ContentCacheInParent::AssignContent(const ContentCache& aOther,
|
|||
mTextRectArray = aOther.mTextRectArray;
|
||||
mEditorRect = aOther.mEditorRect;
|
||||
|
||||
if (mIsComposing) {
|
||||
NS_WARN_IF(mCompositionStart == UINT32_MAX);
|
||||
IMEStateManager::MaybeStartOffsetUpdatedInChild(aWidget, mCompositionStart);
|
||||
} else {
|
||||
NS_WARN_IF(mCompositionStart != UINT32_MAX);
|
||||
}
|
||||
|
||||
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
||||
("ContentCacheInParent: 0x%p AssignContent(aNotification=%s), "
|
||||
"Succeeded, mText.Length()=%u, mSelection={ mAnchor=%u, mFocus=%u, "
|
||||
"mWritingMode=%s, mAnchorCharRect=%s, mFocusCharRect=%s, mRect=%s }, "
|
||||
"mFirstCharRect=%s, mCaret={ mOffset=%u, mRect=%s }, mTextRectArray={ "
|
||||
"mStart=%u, mRects.Length()=%u }, mEditorRect=%s",
|
||||
"mStart=%u, mRects.Length()=%u }, mIsComposing=%s, mCompositionStart=%u, "
|
||||
"mEditorRect=%s",
|
||||
this, GetNotificationName(aNotification),
|
||||
mText.Length(), mSelection.mAnchor, mSelection.mFocus,
|
||||
GetWritingModeName(mSelection.mWritingMode).get(),
|
||||
|
@ -458,7 +470,8 @@ ContentCacheInParent::AssignContent(const ContentCache& aOther,
|
|||
GetRectText(mSelection.mFocusCharRect).get(),
|
||||
GetRectText(mSelection.mRect).get(), GetRectText(mFirstCharRect).get(),
|
||||
mCaret.mOffset, GetRectText(mCaret.mRect).get(), mTextRectArray.mStart,
|
||||
mTextRectArray.mRects.Length(), GetRectText(mEditorRect).get()));
|
||||
mTextRectArray.mRects.Length(), GetBoolName(mIsComposing),
|
||||
mCompositionStart, GetRectText(mEditorRect).get()));
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -835,6 +848,10 @@ ContentCacheInParent::OnCompositionEvent(const WidgetCompositionEvent& aEvent)
|
|||
|
||||
mIsComposing = !aEvent.CausesDOMCompositionEndEvent();
|
||||
|
||||
if (!mIsComposing) {
|
||||
mCompositionStart = UINT32_MAX;
|
||||
}
|
||||
|
||||
// During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION,
|
||||
// widget usually sends a eCompositionChange and/or eCompositionCommit event
|
||||
// to finalize or clear the composition, respectively. In this time,
|
||||
|
|
|
@ -41,6 +41,9 @@ protected:
|
|||
// Whole text in the target
|
||||
nsString mText;
|
||||
|
||||
// Start offset of the composition string.
|
||||
uint32_t mCompositionStart;
|
||||
|
||||
struct Selection final
|
||||
{
|
||||
// Following values are offset in "flat text".
|
||||
|
@ -277,6 +280,7 @@ public:
|
|||
* it's managed by TabParent itself.
|
||||
*/
|
||||
void AssignContent(const ContentCache& aOther,
|
||||
nsIWidget* aWidget,
|
||||
const IMENotification* aNotification = nullptr);
|
||||
|
||||
/**
|
||||
|
@ -360,8 +364,6 @@ private:
|
|||
// composition. Then, data value of dispatched composition events should
|
||||
// be stored into the instance.
|
||||
nsAString* mCommitStringByRequest;
|
||||
// Start offset of the composition string.
|
||||
uint32_t mCompositionStart;
|
||||
// mPendingEventsNeedingAck is increased before sending a composition event or
|
||||
// a selection event and decreased after they are received in the child
|
||||
// process.
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "mozilla/FontRange.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIDOMKeyEvent.h"
|
||||
#include "nsISelectionController.h"
|
||||
#include "nsISelectionListener.h"
|
||||
#include "nsITransferable.h"
|
||||
#include "nsRect.h"
|
||||
|
@ -667,6 +668,15 @@ public:
|
|||
mUseNativeLineBreak = aUseNativeLineBreak;
|
||||
}
|
||||
|
||||
void InitForQuerySelectedText(SelectionType aSelectionType,
|
||||
bool aUseNativeLineBreak = true)
|
||||
{
|
||||
MOZ_ASSERT(mMessage == eQuerySelectedText);
|
||||
MOZ_ASSERT(aSelectionType != SelectionType::eNone);
|
||||
mInput.mSelectionType = aSelectionType;
|
||||
mUseNativeLineBreak = aUseNativeLineBreak;
|
||||
}
|
||||
|
||||
void InitForQueryDOMWidgetHittest(const mozilla::LayoutDeviceIntPoint& aPoint)
|
||||
{
|
||||
NS_ASSERTION(mMessage == eQueryDOMWidgetHittest,
|
||||
|
@ -707,7 +717,7 @@ public:
|
|||
bool mSucceeded;
|
||||
bool mUseNativeLineBreak;
|
||||
bool mWithFontRanges;
|
||||
struct
|
||||
struct Input final
|
||||
{
|
||||
uint32_t EndOffset() const
|
||||
{
|
||||
|
@ -718,9 +728,17 @@ public:
|
|||
|
||||
uint32_t mOffset;
|
||||
uint32_t mLength;
|
||||
SelectionType mSelectionType;
|
||||
|
||||
Input()
|
||||
: mOffset(0)
|
||||
, mLength(0)
|
||||
, mSelectionType(SelectionType::eNormal)
|
||||
{
|
||||
}
|
||||
} mInput;
|
||||
|
||||
struct Reply
|
||||
struct Reply final
|
||||
{
|
||||
void* mContentsRoot;
|
||||
uint32_t mOffset;
|
||||
|
|
|
@ -275,6 +275,16 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool HasClauses() const
|
||||
{
|
||||
for (const TextRange& range : *this) {
|
||||
if (range.IsClause()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t GetCaretPosition() const
|
||||
{
|
||||
for (const TextRange& range : *this) {
|
||||
|
@ -284,6 +294,19 @@ public:
|
|||
}
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
const TextRange* GetFirstClause() const
|
||||
{
|
||||
for (const TextRange& range : *this) {
|
||||
// Look for the range of a clause whose start offset is 0 because the
|
||||
// first clause's start offset is always 0.
|
||||
if (range.IsClause() && !range.mStartOffset) {
|
||||
return ⦥
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(!HasClauses());
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -635,6 +635,30 @@ struct ParamTraits<mozilla::FontRange>
|
|||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::WidgetQueryContentEvent::Input>
|
||||
{
|
||||
typedef mozilla::WidgetQueryContentEvent::Input paramType;
|
||||
typedef mozilla::WidgetQueryContentEvent event;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.mOffset);
|
||||
WriteParam(aMsg, aParam.mLength);
|
||||
WriteParam(aMsg, mozilla::ToRawSelectionType(aParam.mSelectionType));
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
mozilla::RawSelectionType rawSelectionType = 0;
|
||||
bool ok = ReadParam(aMsg, aIter, &aResult->mOffset) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mLength) &&
|
||||
ReadParam(aMsg, aIter, &rawSelectionType);
|
||||
aResult->mSelectionType = mozilla::ToSelectionType(rawSelectionType);
|
||||
return ok;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::WidgetQueryContentEvent>
|
||||
{
|
||||
|
@ -646,8 +670,7 @@ struct ParamTraits<mozilla::WidgetQueryContentEvent>
|
|||
WriteParam(aMsg, aParam.mSucceeded);
|
||||
WriteParam(aMsg, aParam.mUseNativeLineBreak);
|
||||
WriteParam(aMsg, aParam.mWithFontRanges);
|
||||
WriteParam(aMsg, aParam.mInput.mOffset);
|
||||
WriteParam(aMsg, aParam.mInput.mLength);
|
||||
WriteParam(aMsg, aParam.mInput);
|
||||
WriteParam(aMsg, aParam.mReply.mOffset);
|
||||
WriteParam(aMsg, aParam.mReply.mTentativeCaretOffset);
|
||||
WriteParam(aMsg, aParam.mReply.mString);
|
||||
|
@ -665,8 +688,7 @@ struct ParamTraits<mozilla::WidgetQueryContentEvent>
|
|||
ReadParam(aMsg, aIter, &aResult->mSucceeded) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mUseNativeLineBreak) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mWithFontRanges) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mInput.mOffset) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mInput.mLength) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mInput) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mReply.mOffset) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mReply.mTentativeCaretOffset) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mReply.mString) &&
|
||||
|
@ -953,6 +975,7 @@ struct ParamTraits<mozilla::ContentCache>
|
|||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.mCompositionStart);
|
||||
WriteParam(aMsg, aParam.mText);
|
||||
WriteParam(aMsg, aParam.mSelection.mAnchor);
|
||||
WriteParam(aMsg, aParam.mSelection.mFocus);
|
||||
|
@ -970,7 +993,8 @@ struct ParamTraits<mozilla::ContentCache>
|
|||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
return ReadParam(aMsg, aIter, &aResult->mText) &&
|
||||
return ReadParam(aMsg, aIter, &aResult->mCompositionStart) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mText) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mSelection.mAnchor) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mSelection.mFocus) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mSelection.mWritingMode) &&
|
||||
|
|
|
@ -166,6 +166,49 @@ function checkSelection(aExpectedOffset, aExpectedText, aMessage, aID)
|
|||
selectedText.text == aExpectedText;
|
||||
}
|
||||
|
||||
function checkIMESelection(aSelectionType, aExpectedFound, aExpectedOffset, aExpectedText, aMessage, aID)
|
||||
{
|
||||
if (!aID) {
|
||||
aID = "";
|
||||
}
|
||||
aMessage += " (" + aSelectionType + ")";
|
||||
var selectionType = 0;
|
||||
switch (aSelectionType) {
|
||||
case "RawClause":
|
||||
selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_RAWINPUT;
|
||||
break;
|
||||
case "SelectedRawClause":
|
||||
selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDRAWTEXT;
|
||||
break;
|
||||
case "ConvertedClause":
|
||||
selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_CONVERTEDTEXT;
|
||||
break;
|
||||
case "SelectedClause":
|
||||
selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDCONVERTEDTEXT;
|
||||
break;
|
||||
default:
|
||||
ok(false, aMessage + ": invalid selection type, " + aSelectionType);
|
||||
}
|
||||
isnot(selectionType, 0, aMessage + ": wrong value");
|
||||
var selectedText = synthesizeQuerySelectedText(selectionType);
|
||||
if (!checkQueryContentResult(selectedText, aMessage +
|
||||
": synthesizeQuerySelectedText " + aID)) {
|
||||
return false;
|
||||
}
|
||||
is(selectedText.notFound, !aExpectedFound,
|
||||
aMessage + ": selection should " + (aExpectedFound ? "" : "not") + " be found " + aID);
|
||||
if (selectedText.notFound) {
|
||||
return selectedText.notFound == !aExpectedFound;
|
||||
}
|
||||
|
||||
is(selectedText.offset, aExpectedOffset,
|
||||
aMessage + ": selection offset is wrong " + aID);
|
||||
is(selectedText.text, aExpectedText,
|
||||
aMessage + ": selected text is wrong " + aID);
|
||||
return selectedText.offset == aExpectedOffset &&
|
||||
selectedText.text == aExpectedText;
|
||||
}
|
||||
|
||||
function checkRect(aRect, aExpectedRect, aMessage)
|
||||
{
|
||||
is(aRect.left, aExpectedRect.left, aMessage + ": left is wrong");
|
||||
|
@ -3408,6 +3451,140 @@ function runQueryTextContentEventTest()
|
|||
is(result.text, "abc", "runQueryTextContentEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\"");
|
||||
}
|
||||
|
||||
function runQueryIMESelectionTest()
|
||||
{
|
||||
textarea.focus();
|
||||
textarea.value = "before after";
|
||||
var startoffset = textarea.selectionStart = textarea.selectionEnd = "before ".length;
|
||||
|
||||
if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: before starting composition") ||
|
||||
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: before starting composition") ||
|
||||
!checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: before starting composition") ||
|
||||
!checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: before starting composition")) {
|
||||
synthesizeComposition({ type: "compositioncommitasis" });
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeCompositionChange(
|
||||
{ "composition":
|
||||
{ "string": "a",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 1, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkIMESelection("RawClause", true, startoffset, "a", "runQueryIMESelectionTest: inputting raw text") ||
|
||||
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: inputting raw text") ||
|
||||
!checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: inputting raw text") ||
|
||||
!checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: inputting raw text")) {
|
||||
synthesizeComposition({ type: "compositioncommitasis" });
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeCompositionChange(
|
||||
{ "composition":
|
||||
{ "string": "abcdefgh",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 8, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 8, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkIMESelection("RawClause", true, startoffset, "abcdefgh", "runQueryIMESelectionTest: updating raw text") ||
|
||||
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: updating raw text") ||
|
||||
!checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: updating raw text") ||
|
||||
!checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: updating raw text")) {
|
||||
synthesizeComposition({ type: "compositioncommitasis" });
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeCompositionChange(
|
||||
{ "composition":
|
||||
{ "string": "ABCDEFGH",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
||||
{ "length": 3, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
||||
{ "length": 3, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
||||
]
|
||||
},
|
||||
"caret": { "start": 2, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: starting to convert") ||
|
||||
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: starting to convert") ||
|
||||
!checkIMESelection("ConvertedClause", true, startoffset + 2, "CDE", "runQueryIMESelectionTest: starting to convert") ||
|
||||
!checkIMESelection("SelectedClause", true, startoffset, "AB", "runQueryIMESelectionTest: starting to convert")) {
|
||||
synthesizeComposition({ type: "compositioncommitasis" });
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeCompositionChange(
|
||||
{ "composition":
|
||||
{ "string": "ABCDEFGH",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 2, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
||||
{ "length": 3, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
||||
{ "length": 3, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
||||
]
|
||||
},
|
||||
"caret": { "start": 5, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: changing selected clause") ||
|
||||
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: changing selected clause") ||
|
||||
!checkIMESelection("ConvertedClause", true, startoffset, "AB", "runQueryIMESelectionTest: changing selected clause") ||
|
||||
!checkIMESelection("SelectedClause", true, startoffset + 2, "CDE", "runQueryIMESelectionTest: changing selected clause")) {
|
||||
synthesizeComposition({ type: "compositioncommitasis" });
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeComposition({ type: "compositioncommitasis" });
|
||||
|
||||
if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: after committing composition") ||
|
||||
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: after committing composition") ||
|
||||
!checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: after committing composition") ||
|
||||
!checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: after committing composition")) {
|
||||
return;
|
||||
}
|
||||
|
||||
startoffset = textarea.selectionStart;
|
||||
|
||||
synthesizeCompositionChange(
|
||||
{ "composition":
|
||||
{ "string": "abcdefgh",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE },
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_SELECTED_RAW_CLAUSE },
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE },
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_SELECTED_RAW_CLAUSE },
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
||||
]
|
||||
},
|
||||
"caret": { "start": 8, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkIMESelection("RawClause", true, startoffset, "a", "runQueryIMESelectionTest: unrealistic testcase") ||
|
||||
!checkIMESelection("SelectedRawClause", true, startoffset + 1, "b", "runQueryIMESelectionTest: unrealistic testcase") ||
|
||||
!checkIMESelection("ConvertedClause", true, startoffset + 2, "c", "runQueryIMESelectionTest: unrealistic testcase") ||
|
||||
!checkIMESelection("SelectedClause", true, startoffset + 3, "d", "runQueryIMESelectionTest: unrealistic testcase")) {
|
||||
synthesizeComposition({ type: "compositioncommitasis" });
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeComposition({ type: "compositioncommitasis" });
|
||||
}
|
||||
|
||||
function runCSSTransformTest()
|
||||
{
|
||||
textarea.focus();
|
||||
|
@ -5751,6 +5928,7 @@ function runTest()
|
|||
runCharAtPointAtOutsideTest();
|
||||
runSetSelectionEventTest();
|
||||
runQueryTextContentEventTest();
|
||||
runQueryIMESelectionTest();
|
||||
runCSSTransformTest();
|
||||
runBug722639Test();
|
||||
runForceCommitTest();
|
||||
|
|
Загрузка…
Ссылка в новой задаче