mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 23:02:20 +00:00
Bug 569334 part 1 - Support getting font info in content query. r=masayuki,jfkthame,smaug
This commit is contained in:
parent
ea3d579f8e
commit
6a7ee49055
@ -314,6 +314,17 @@ ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
|
||||
return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
GetBRLength(LineBreakType aLineBreakType)
|
||||
{
|
||||
#if defined(XP_WIN)
|
||||
// Length of \r\n
|
||||
return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* static */ uint32_t
|
||||
ContentEventHandler::GetTextLength(nsIContent* aContent,
|
||||
LineBreakType aLineBreakType,
|
||||
@ -344,12 +355,7 @@ ContentEventHandler::GetTextLength(nsIContent* aContent,
|
||||
uint32_t length = std::min(text->GetLength(), aMaxLength);
|
||||
return length + textLengthDifference;
|
||||
} else if (IsContentBR(aContent)) {
|
||||
#if defined(XP_WIN)
|
||||
// Length of \r\n
|
||||
return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
return GetBRLength(aLineBreakType);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -393,7 +399,6 @@ static nsresult GenerateFlatTextContent(nsRange* aRange,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString tmpStr;
|
||||
for (; !iter->IsDone(); iter->Next()) {
|
||||
nsINode* node = iter->GetCurrentNode();
|
||||
if (!node) {
|
||||
@ -423,6 +428,171 @@ static nsresult GenerateFlatTextContent(nsRange* aRange,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static FontRange*
|
||||
AppendFontRange(nsTArray<FontRange>& aFontRanges, uint32_t aBaseOffset)
|
||||
{
|
||||
FontRange* fontRange = aFontRanges.AppendElement();
|
||||
fontRange->mStartOffset = aBaseOffset;
|
||||
return fontRange;
|
||||
}
|
||||
|
||||
/* static */ uint32_t
|
||||
ContentEventHandler::GetTextLengthInRange(nsIContent* aContent,
|
||||
uint32_t aXPStartOffset,
|
||||
uint32_t aXPEndOffset,
|
||||
LineBreakType aLineBreakType)
|
||||
{
|
||||
return aLineBreakType == LINE_BREAK_TYPE_NATIVE ?
|
||||
GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) :
|
||||
aXPEndOffset - aXPStartOffset;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
|
||||
nsIContent* aContent,
|
||||
int32_t aBaseOffset,
|
||||
int32_t aXPStartOffset,
|
||||
int32_t aXPEndOffset,
|
||||
LineBreakType aLineBreakType)
|
||||
{
|
||||
nsIFrame* frame = aContent->GetPrimaryFrame();
|
||||
if (!frame) {
|
||||
// It is a non-rendered content, create an empty range for it.
|
||||
AppendFontRange(aFontRanges, aBaseOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t baseOffset = aBaseOffset;
|
||||
nsTextFrame* curr = do_QueryFrame(frame);
|
||||
MOZ_ASSERT(curr, "Not a text frame");
|
||||
while (curr) {
|
||||
int32_t frameXPStart = std::max(curr->GetContentOffset(), aXPStartOffset);
|
||||
int32_t frameXPEnd = std::min(curr->GetContentEnd(), aXPEndOffset);
|
||||
if (frameXPStart >= frameXPEnd) {
|
||||
curr = static_cast<nsTextFrame*>(curr->GetNextContinuation());
|
||||
continue;
|
||||
}
|
||||
|
||||
gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated);
|
||||
gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated);
|
||||
|
||||
nsTextFrame* next = nullptr;
|
||||
if (frameXPEnd < aXPEndOffset) {
|
||||
next = static_cast<nsTextFrame*>(curr->GetNextContinuation());
|
||||
while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) {
|
||||
frameXPEnd = std::min(next->GetContentEnd(), aXPEndOffset);
|
||||
next = frameXPEnd < aXPEndOffset ?
|
||||
static_cast<nsTextFrame*>(next->GetNextContinuation()) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t skipStart = iter.ConvertOriginalToSkipped(frameXPStart);
|
||||
uint32_t skipEnd = iter.ConvertOriginalToSkipped(frameXPEnd);
|
||||
gfxTextRun::GlyphRunIterator runIter(
|
||||
textRun, skipStart, skipEnd - skipStart);
|
||||
int32_t lastXPEndOffset = frameXPStart;
|
||||
while (runIter.NextRun()) {
|
||||
gfxFont* font = runIter.GetGlyphRun()->mFont.get();
|
||||
int32_t startXPOffset =
|
||||
iter.ConvertSkippedToOriginal(runIter.GetStringStart());
|
||||
// It is possible that the first glyph run has exceeded the frame,
|
||||
// because the whole frame is filled by skipped chars.
|
||||
if (startXPOffset >= frameXPEnd) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (startXPOffset > lastXPEndOffset) {
|
||||
// Create range for skipped leading chars.
|
||||
AppendFontRange(aFontRanges, baseOffset);
|
||||
baseOffset += GetTextLengthInRange(
|
||||
aContent, lastXPEndOffset, startXPOffset, aLineBreakType);
|
||||
lastXPEndOffset = startXPOffset;
|
||||
}
|
||||
|
||||
FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
|
||||
fontRange->mFontName = font->GetName();
|
||||
fontRange->mFontSize = font->GetAdjustedSize();
|
||||
|
||||
// The converted original offset may exceed the range,
|
||||
// hence we need to clamp it.
|
||||
int32_t endXPOffset =
|
||||
iter.ConvertSkippedToOriginal(runIter.GetStringEnd());
|
||||
endXPOffset = std::min(frameXPEnd, endXPOffset);
|
||||
baseOffset += GetTextLengthInRange(aContent, startXPOffset, endXPOffset,
|
||||
aLineBreakType);
|
||||
lastXPEndOffset = endXPOffset;
|
||||
}
|
||||
if (lastXPEndOffset < frameXPEnd) {
|
||||
// Create range for skipped trailing chars. It also handles case
|
||||
// that the whole frame contains only skipped chars.
|
||||
AppendFontRange(aFontRanges, baseOffset);
|
||||
baseOffset += GetTextLengthInRange(
|
||||
aContent, lastXPEndOffset, frameXPEnd, aLineBreakType);
|
||||
}
|
||||
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
|
||||
FontRangeArray& aFontRanges,
|
||||
uint32_t& aLength,
|
||||
LineBreakType aLineBreakType)
|
||||
{
|
||||
MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array");
|
||||
|
||||
nsINode* startNode = aRange->GetStartParent();
|
||||
nsINode* endNode = aRange->GetEndParent();
|
||||
if (NS_WARN_IF(!startNode || !endNode)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// baseOffset is the flattened offset of each content node.
|
||||
int32_t baseOffset = 0;
|
||||
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
|
||||
for (iter->Init(aRange); !iter->IsDone(); iter->Next()) {
|
||||
nsINode* node = iter->GetCurrentNode();
|
||||
if (NS_WARN_IF(!node)) {
|
||||
break;
|
||||
}
|
||||
if (!node->IsContent()) {
|
||||
continue;
|
||||
}
|
||||
nsIContent* content = node->AsContent();
|
||||
|
||||
if (content->IsNodeOfType(nsINode::eTEXT)) {
|
||||
int32_t startOffset = content != startNode ? 0 : aRange->StartOffset();
|
||||
int32_t endOffset = content != endNode ?
|
||||
content->TextLength() : aRange->EndOffset();
|
||||
AppendFontRanges(aFontRanges, content, baseOffset,
|
||||
startOffset, endOffset, aLineBreakType);
|
||||
baseOffset += GetTextLengthInRange(content, startOffset, endOffset,
|
||||
aLineBreakType);
|
||||
} else if (IsContentBR(content)) {
|
||||
if (aFontRanges.IsEmpty()) {
|
||||
MOZ_ASSERT(baseOffset == 0);
|
||||
FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
|
||||
nsIFrame* frame = content->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
const nsFont& font = frame->GetParent()->StyleFont()->mFont;
|
||||
const FontFamilyList& fontList = font.fontlist;
|
||||
const FontFamilyName& fontName = fontList.IsEmpty() ?
|
||||
FontFamilyName(fontList.GetDefaultFontType()) :
|
||||
fontList.GetFontlist()[0];
|
||||
fontName.AppendToString(fontRange->mFontName, false);
|
||||
fontRange->mFontSize =
|
||||
frame->PresContext()->AppUnitsToDevPixels(font.size);
|
||||
}
|
||||
}
|
||||
baseOffset += GetBRLength(aLineBreakType);
|
||||
}
|
||||
}
|
||||
|
||||
aLength = baseOffset;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
|
||||
bool aForward,
|
||||
@ -697,6 +867,18 @@ ContentEventHandler::OnQueryTextContent(WidgetQueryContentEvent* aEvent)
|
||||
rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aEvent->mWithFontRanges) {
|
||||
uint32_t fontRangeLength;
|
||||
rv = GenerateFlatFontRanges(range, aEvent->mReply.mFontRanges,
|
||||
fontRangeLength, lineBreakType);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(fontRangeLength == aEvent->mReply.mString.Length(),
|
||||
"Font ranges doesn't match the string");
|
||||
}
|
||||
|
||||
aEvent->mSucceeded = true;
|
||||
|
||||
return NS_OK;
|
||||
|
@ -94,6 +94,12 @@ public:
|
||||
// Get the native text length of a content node excluding any children
|
||||
static uint32_t GetNativeTextLength(nsIContent* aContent,
|
||||
uint32_t aMaxLength = UINT32_MAX);
|
||||
// Get the text length of a given range of a content node in
|
||||
// the given line break type.
|
||||
static uint32_t GetTextLengthInRange(nsIContent* aContent,
|
||||
uint32_t aXPStartOffset,
|
||||
uint32_t aXPEndOffset,
|
||||
LineBreakType aLineBreakType);
|
||||
protected:
|
||||
static uint32_t GetTextLength(nsIContent* aContent,
|
||||
LineBreakType aLineBreakType,
|
||||
@ -129,6 +135,18 @@ protected:
|
||||
// true, it is expanded to forward.
|
||||
nsresult ExpandToClusterBoundary(nsIContent* aContent, bool aForward,
|
||||
uint32_t* aXPOffset);
|
||||
|
||||
typedef nsTArray<mozilla::FontRange> FontRangeArray;
|
||||
static void AppendFontRanges(FontRangeArray& aFontRanges,
|
||||
nsIContent* aContent,
|
||||
int32_t aBaseOffset,
|
||||
int32_t aXPStartOffset,
|
||||
int32_t aXPEndOffset,
|
||||
LineBreakType aLineBreakType);
|
||||
static nsresult GenerateFlatFontRanges(nsRange* aRange,
|
||||
FontRangeArray& aFontRanges,
|
||||
uint32_t& aLength,
|
||||
LineBreakType aLineBreakType);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -544,6 +544,12 @@ struct ParamTraits<InfallibleTArray<E> >
|
||||
}
|
||||
};
|
||||
|
||||
template<typename E, size_t N>
|
||||
struct ParamTraits<nsAutoTArray<E, N>> : ParamTraits<nsTArray<E>>
|
||||
{
|
||||
typedef nsAutoTArray<E, N> paramType;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<float>
|
||||
{
|
||||
|
@ -112,6 +112,9 @@ struct TextRange;
|
||||
|
||||
class TextRangeArray;
|
||||
|
||||
// FontRange.h
|
||||
struct FontRange;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_EventForwards_h__
|
||||
|
27
widget/FontRange.h
Normal file
27
widget/FontRange.h
Normal file
@ -0,0 +1,27 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_FontRange_h_
|
||||
#define mozilla_FontRange_h_
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct FontRange
|
||||
{
|
||||
FontRange()
|
||||
: mStartOffset(0)
|
||||
, mFontSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
int32_t mStartOffset;
|
||||
nsString mFontName;
|
||||
gfxFloat mFontSize; // in device pixels
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // mozilla_FontRange_h_
|
@ -12,6 +12,7 @@
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/EventForwards.h" // for KeyNameIndex, temporarily
|
||||
#include "mozilla/TextRange.h"
|
||||
#include "mozilla/FontRange.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIDOMKeyEvent.h"
|
||||
#include "nsITransferable.h"
|
||||
@ -399,6 +400,7 @@ public:
|
||||
, mSucceeded(false)
|
||||
, mWasAsync(false)
|
||||
, mUseNativeLineBreak(true)
|
||||
, mWithFontRanges(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -447,6 +449,13 @@ public:
|
||||
refPoint = aPoint;
|
||||
}
|
||||
|
||||
void RequestFontRanges()
|
||||
{
|
||||
NS_ASSERTION(message == NS_QUERY_TEXT_CONTENT,
|
||||
"not querying text content");
|
||||
mWithFontRanges = true;
|
||||
}
|
||||
|
||||
uint32_t GetSelectionStart(void) const
|
||||
{
|
||||
NS_ASSERTION(message == NS_QUERY_SELECTED_TEXT,
|
||||
@ -471,6 +480,7 @@ public:
|
||||
bool mSucceeded;
|
||||
bool mWasAsync;
|
||||
bool mUseNativeLineBreak;
|
||||
bool mWithFontRanges;
|
||||
struct
|
||||
{
|
||||
uint32_t mOffset;
|
||||
@ -495,6 +505,8 @@ public:
|
||||
mozilla::WritingMode mWritingMode;
|
||||
// used by NS_QUERY_SELECTION_AS_TRANSFERABLE
|
||||
nsCOMPtr<nsITransferable> mTransferable;
|
||||
// used by NS_QUERY_TEXT_CONTENT with font ranges requested
|
||||
nsAutoTArray<mozilla::FontRange, 1> mFontRanges;
|
||||
} mReply;
|
||||
|
||||
enum
|
||||
|
@ -117,6 +117,7 @@ EXPORTS.mozilla += [
|
||||
'ContentEvents.h',
|
||||
'EventClassList.h',
|
||||
'EventForwards.h',
|
||||
'FontRange.h',
|
||||
'LookAndFeel.h',
|
||||
'MiscEvents.h',
|
||||
'MouseEvents.h',
|
||||
|
@ -525,6 +525,26 @@ struct ParamTraits<mozilla::WidgetCompositionEvent>
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::FontRange>
|
||||
{
|
||||
typedef mozilla::FontRange paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.mStartOffset);
|
||||
WriteParam(aMsg, aParam.mFontName);
|
||||
WriteParam(aMsg, aParam.mFontSize);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
{
|
||||
return ReadParam(aMsg, aIter, &aResult->mStartOffset) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mFontName) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mFontSize);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::WidgetQueryContentEvent>
|
||||
{
|
||||
@ -535,6 +555,7 @@ struct ParamTraits<mozilla::WidgetQueryContentEvent>
|
||||
WriteParam(aMsg, static_cast<mozilla::WidgetGUIEvent>(aParam));
|
||||
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.mReply.mOffset);
|
||||
@ -543,6 +564,7 @@ struct ParamTraits<mozilla::WidgetQueryContentEvent>
|
||||
WriteParam(aMsg, aParam.mReply.mReversed);
|
||||
WriteParam(aMsg, aParam.mReply.mHasSelection);
|
||||
WriteParam(aMsg, aParam.mReply.mWidgetIsHit);
|
||||
WriteParam(aMsg, aParam.mReply.mFontRanges);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
@ -552,6 +574,7 @@ struct ParamTraits<mozilla::WidgetQueryContentEvent>
|
||||
static_cast<mozilla::WidgetGUIEvent*>(aResult)) &&
|
||||
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->mReply.mOffset) &&
|
||||
@ -559,7 +582,8 @@ struct ParamTraits<mozilla::WidgetQueryContentEvent>
|
||||
ReadParam(aMsg, aIter, &aResult->mReply.mRect) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mReply.mReversed) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mReply.mHasSelection) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mReply.mWidgetIsHit);
|
||||
ReadParam(aMsg, aIter, &aResult->mReply.mWidgetIsHit) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mReply.mFontRanges);
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user