/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ContentEventHandler.h" #include "mozilla/IMEStateManager.h" #include "mozilla/TextComposition.h" #include "mozilla/TextEvents.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/HTMLUnknownElement.h" #include "mozilla/dom/Selection.h" #include "nsCaret.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsCopySupport.h" #include "nsFocusManager.h" #include "nsFontMetrics.h" #include "nsFrameSelection.h" #include "nsIContentIterator.h" #include "nsIPresShell.h" #include "nsISelection.h" #include "nsIFrame.h" #include "nsIObjectFrame.h" #include "nsLayoutUtils.h" #include "nsPresContext.h" #include "nsQueryObject.h" #include "nsRange.h" #include "nsTextFragment.h" #include "nsTextFrame.h" #include "nsView.h" #include namespace mozilla { using namespace dom; using namespace widget; /******************************************************************/ /* ContentEventHandler */ /******************************************************************/ // NOTE // // ContentEventHandler *creates* ranges as following rules: // 1. Start of range: // 1.1. Cases: [textNode or text[Node or textNode[ // When text node is start of a range, start node is the text node and // start offset is any number between 0 and the length of the text. // 1.2. Case: [: // When start of an element node is start of a range, start node is // parent of the element and start offset is the element's index in the // parent. // 1.3. Case: [ // When after an empty element node is start of a range, start node is // parent of the element and start offset is the element's index in the // parent + 1. // 1.4. Case: [ // When start of a non-empty element is start of a range, start node is // the element and start offset is 0. // 1.5. Case: [ // When start of a range is 0 and there are no nodes causing text, // start node is the root node and start offset is 0. // 1.6. Case: [ // When start of a range is out of bounds, start node is the root node // and start offset is number of the children. // 2. End of range: // 2.1. Cases: ]textNode or text]Node or textNode] // When a text node is end of a range, end node is the text node and // end offset is any number between 0 and the length of the text. // 2.2. Case: ] // When before an element node (meaning before the open tag of the // element) is end of a range, end node is previous node causing text. // Note that this case shouldn't be handled directly. If rule 2.1 and // 2.3 are handled correctly, the loop with nsContentIterator shouldn't // reach the element node since the loop should've finished already at // handling the last node which caused some text. // 2.3. Case: ] // When a line break is caused before a non-empty element node and it's // end of a range, end node is the element and end offset is 0. // (i.e., including open tag of the element) // 2.4. Cases: ] // When after an empty element node is end of a range, end node is // parent of the element node and end offset is the element's index in // the parent + 1. (i.e., including close tag of the element or empty // element) // 2.5. Case: ] // When end of a range is out of bounds, end node is the root node and // end offset is number of the children. // // ContentEventHandler *treats* ranges as following additional rules: // 1. When the start node is an element node which doesn't have children, // it includes a line break caused before itself (i.e., includes its open // tag). For example, if start position is {
, 0 }, the line break // caused by
should be included into the flatten text. // 2. When the end node is an element node which doesn't have children, // it includes the end (i.e., includes its close tag except empty element). // Although, currently, any close tags don't cause line break, this also // includes its open tag. For example, if end position is {
, 0 }, the // line break caused by the
should be included into the flatten text. ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext) : mPresContext(aPresContext) , mPresShell(aPresContext->GetPresShell()) , mSelection(nullptr) , mFirstSelectedRange(nullptr) , mRootContent(nullptr) { } nsresult ContentEventHandler::InitBasic() { NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE); // If text frame which has overflowing selection underline is dirty, // we need to flush the pending reflow here. if (nsIDocument* doc = mPresShell->GetDocument()) { doc->FlushPendingNotifications(Flush_Layout); } return NS_OK; } nsresult ContentEventHandler::InitRootContent(Selection* aNormalSelection) { MOZ_ASSERT(aNormalSelection); // 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); if (!aNormalSelection->RangeCount()) { // If there is no selection range, we should compute the selection root // from ancestor limiter or root content of the document. nsresult rv = aNormalSelection->GetAncestorLimiter(getter_AddRefs(mRootContent)); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } if (!mRootContent) { mRootContent = mPresShell->GetDocument()->GetRootElement(); if (NS_WARN_IF(!mRootContent)) { return NS_ERROR_NOT_AVAILABLE; } } return NS_OK; } RefPtr 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 EditorBase but the // selection range still keeps storing the nodes. If the active element of // the deactive window is or