Merge inbound to central, a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2016-06-21 10:05:54 -07:00
Родитель 2488f506e6 a25ec1d6dc
Коммит 5c26bde4f0
64 изменённых файлов: 1072 добавлений и 238 удалений

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

@ -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,40 +149,121 @@ ContentEventHandler::InitCommon()
return NS_ERROR_NOT_AVAILABLE;
}
}
// Assume that there is selection at beginning of the root content.
rv = nsRange::CreateRange(mRootContent, 0, mRootContent, 0,
getter_AddRefs(mFirstSelectedRange));
if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!mFirstSelectedRange)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
mFirstSelectedRange = mSelection->GetRangeAt(0);
if (NS_WARN_IF(!mFirstSelectedRange)) {
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 mSelection was cleared by blur event handler of nsEditor but the
// 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 = mFirstSelectedRange->GetStartParent();
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
nsINode* endNode = mFirstSelectedRange->GetEndParent();
NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
// 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.
NS_ENSURE_TRUE(startNode->GetUncomposedDoc() == mPresShell->GetDocument(),
NS_ERROR_NOT_AVAILABLE);
if (NS_WARN_IF(startNode->GetUncomposedDoc() != mPresShell->GetDocument())) {
return NS_ERROR_FAILURE;
}
NS_ASSERTION(startNode->GetUncomposedDoc() == endNode->GetUncomposedDoc(),
"mFirstSelectedRange crosses the document boundary");
"firstNormalSelectionRange crosses the document boundary");
mRootContent = startNode->GetSelectionRootContent(mPresShell);
NS_ENSURE_TRUE(mRootContent, NS_ERROR_FAILURE);
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)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
@ -197,8 +271,19 @@ 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,37 +1236,33 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
nsCOMPtr<nsINode> anchorNode, focusNode;
int32_t anchorOffset, focusOffset;
if (mSelection->RangeCount()) {
anchorNode = mSelection->GetAnchorNode();
focusNode = mSelection->GetFocusNode();
if (NS_WARN_IF(!anchorNode) || NS_WARN_IF(!focusNode)) {
return NS_ERROR_FAILURE;
}
anchorOffset = static_cast<int32_t>(mSelection->AnchorOffset());
focusOffset = static_cast<int32_t>(mSelection->FocusOffset());
if (NS_WARN_IF(anchorOffset < 0) || NS_WARN_IF(focusOffset < 0)) {
return NS_ERROR_FAILURE;
}
int16_t compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
focusNode, focusOffset);
aEvent->mReply.mReversed = compare > 0;
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;
}
// 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)) {
return NS_ERROR_FAILURE;
}
rv = GenerateFlatTextContent(range, aEvent->mReply.mString,
anchorOffset = static_cast<int32_t>(mSelection->AnchorOffset());
focusOffset = static_cast<int32_t>(mSelection->FocusOffset());
if (NS_WARN_IF(anchorOffset < 0) || NS_WARN_IF(focusOffset < 0)) {
return NS_ERROR_FAILURE;
}
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 (!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;
if (!IsValidStateForComposition(aCompositionEvent->mWidget)) {
return;
}
// 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;
}
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">&#x0CC4;&#x0CA7;&#x200C;</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>&#x05D0;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&amp;
>>>>><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 &range;
}
}
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();