mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 23:06:52 +00:00
8a0a002cf2
- Bug 1148708: Add missing 'override' annotations in DocAccessibleChild.h. rs=ehsan (d606358545)
- Bug 1210408 - make nsMaiInterfaceAction work with proxies, r=tbsaunde (f7c819c6ae)
- Bug 1210407 - teach nsMaiInterfaceTable to use proxies, r=tbsaunde (4ca4f10b5f)
- bug 1185157 make sure we don't send an event to a destroyed ipc document r=billm (23acf53f75)
- bug 1214864 - make SetCarretOffset() async r=davidb (e3079e9b2d)
- missing of Bug 1139972 - IPC Proxy for charAt, r=tbsaunde (e9593ed752)
- bug 1191598 - Pass MOZ_CURRENT_PROJECT in environment when running post-build automation steps for universal mac builds. r=gps (fc342c6ced)
- Bug 1164596 - Add mach android-emulator command; r=ahal (afeb9b27d1)
- Bug 1223149 - Add basic usage documentation for mach build; r=glandium (bfb802d175)
- Bug 1182301 - Improve 'mach build' notifications. r=gps (2c65a122d1)
- Bug 1184696 - Add clobber targets to |mach clobber|; Ability to clobber compiled python files, r=gps (35d8be292e)
- Bug 1117958 - Allow any debugging options to the run or gtest mach subcommands to automatically enable debugging. r=gps (32f986af4b)
- Bug 1180081 - Properly rebuild gtest/libxul before running gtests. r=gps (80db9a3d49)
- Bug 1171647, part 1 - Define a new function to convert the mode to a string. r=njn (61ad16f5ba)
- Bug 1171647, part 2 - Remove redundant assertion for dark matter mode. r=njn (b5ac9519f3)
- Bug 1058178, part 1 - Implement DMD heap scanning mode. r=njn (60e1079536)
- Bug 1058178, part 2 - Implement address clamping analysis for DMD scan logs. r=njn (45c0326b93)
- Bug 1102388 - Fix DMD static constructor ordering dependency. r=mccr8 (59b87897a1)
- Bug 1128705 - Don't redefine PAGE_SIZE in DMD if it's already defined. r=erahm (49216348ee)
- Bug 1179042 - Add a script for analyzing memory blocks using a heap scan DMD log. r=njn DONTBUILD (1c08d2d66e)
- Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat (1c999d139b)
- Bug 1158772 - fix non-idiomatic memset call in nsDeque.cpp; r=erahm (de6b555245)
- Bug 1199400 - Part 1: Use CheckedInt when growing nsDeque capacity. r=froydnj (dfdf6814a3)
- Bug 1199400 - Part 0: Remove unused nsDequeIterator. r=froydnj (38d69d7f47)
- Bug 1199400 - Part 2: Add tests for possible nsDeque corner cases. r=froydnj (931baff195)
- Bug 1201997 - Part 1 - Converted compiled test to gtest for nsDeque class. Added tests to test untested methods. r=froydn (e893916651)
- Bug 1201997 - Part 2 - Removing unused methods from the nsDeque class. r=froydn (41595a90ac)
- Bug 1201997 - Part 3 - Make internally used methods private. r=froydn (8cd3afd96f)
- Bug 1201997 - Part 4 - Change size and offset variables to size_t.r=froydn (73eabc8d60)
- Bug 1215140 P1 Add an nsIConsoleReportCollector interface to support navigation channel logging. r=bz (8a41535e2b)
- Bug 1215140 P2 Make HttpBaseChannel implement nsIConsoleReportCollector. r=bz (75fca301f2)
- Bug 1197679 - If nsUnknownDecoder is involved in e10s DivertToParent can break. r=jduell (5d94a12504)
- Bug 1178991 - smartptr for http converter r=hurley (8e7fbc8443)
- bug 366559 - patch 2, fix nsHTTPCompressConv indentation r=bagder (ba762da587)
- bug 366559 - patch 3, fix nsHTTPCompressConv bracing style r=bagder (54195ab451)
- bits of bug 366559 - patch 7, content-encoding brotli for http (f0b4051022)
- Bug 1205112 - Make PushEvent.data nullable. r=mt,smaug (775db32856)
- Bug 1193414 - SharedWorkers thread should be kept alive also when the SharedWorker object is CCed, r=khuey (b77ea8125c)
- Bug 1206520: Add about:config prefs to enable throwing on asm.js validation failures; r=bz (c42126665d)
- Bug 1193414 - Telemetry for SharedWorker spawning. r=bkelly (77984b7bcc)
- Bug 1205676 - Enable WPT service-worker/unregister-then-register-new-script.https.html in e10s, r=nsm (ec24939cf6)
- Bug 1193133 - Throw when calling postMessage from a Service Worker dom object with no global. r=bkelly (526dcacfab)
- Bug 1181871 P1 Only enforce Cache Context shared data destruction on target thread after init. r=ehsan (cdbf3ed3a8)
- Bug 1181871 P2 Fix ServiceWorkerManager usage of stack-based ErrorResult. r=ehsan (c449195d90)
- minor cleanup and missing bit of 1198230 (02f459db05)
- Bug 1143717 - Implement the ServiceWorkerMessageEvent interface. r=baku (027b3465f2)
- fix misspatch (708eee4e84)
- Bug 1188545 - Disentangle service workers from shared workers and refactor event dispatching code into a separate class. r=nsm,mrbkap (fb5b5341c9)
- Bug 1205228 - Change PackagedAppVerifier to notify the verification result asynchronously. r=valentin. (9edda0fa00)
- Bug 1178518 - Packaged App Utils. r=valentin (f60f3b7a93)
- Bug 1213150 - Part 1: Add a nsContentUtils::IsNonSubresourceRequest helper; r=jdm (b509cc3cc9)
- Bug 1213150 - Part 2: Rework ShouldPrepareForIntercept() in terms of subresource requests; r=jdm (2e92fe8780)
- Bug 1213150 - Part 3: Remove nsIInterceptedChannel.isNavigation; r=jdm (becf1cc12f)
- Bug 1213150 follow-up: fix build bustage (8d73d6ca73)
- Bug 1198394 - Part 1: Allow interception of HSTS upgraded connections in non-e10s mode; r=mcmanus (f504c5be08)
- Bug 1198394 - Part 2: Add a test for interception of HSTS upgraded connections; r=jdm (054e984eef)
- Bug 1187011 - Don't allow response body with null body status. r=bkelly (b1860741d1)
- missing bit of 1140788 (29d319712e)
- Bug 1213436 - Reject core dumps with node IDs that don't fit in an IEEE 754 double; r=sfink (3c1f6fdda0)
- Bug 1211006 - Add Debugger.Source.prototype.canonicalId; r=ejpbruel (eef7b79fce)
- Bug 1199218 - Implement JS::ubi::Node::size for js::LazyScript referents; r=sfink (098a48d240)
- Bug 1220031 - Add JS::ubi::Node::scriptFilename; r=sfink (6b824ae680)
- Bug 1143575. Remove unused MediaQueue::Empty. r=cpearce (de737f3433)
- Bug 1209933 - Make sure all parent runtime pointers are the topmost parent, r=billm. (fe824d967d)
- Bug 1197012 - Fix ThrowTypeError in Notification. r=mccr8 (0b1a097526)
- Bug 1197893 - Check the number of arguments for ThrowTypeError() and ThrowRangeError() at compile time. r=peterv (d98c7d78a0)
- Bug 1142083 - Add test for IDN Unicode domain redirect. r=mcmanus (0c8961fe17)
- Bug 1187159 - Add mochitest for loading packaged apps (iframe+fetch+mozapp) r=jduell (ce90ea561b)
- Bug 1186290 - Notify TabParent to switch process when loading a signed package. r=honzab, r=kanru. (c58a14554a)
- fix (15e2df75eb)
- Bug 1206124 P1 Fix "same-origin" CORS credentials in FetchDriver. r=ehsan (fae1bb6ab3)
- Bug 1206124 P2 Test fetch() with credentials and redirects. r=ehsan (ffc6254112)
- Bug 1211751: Remove nsIChannelEventSink-forwarding from EventSource and FetchDriver. It's never needed. r=smaug (adafe5737a)
- Bug 1212433 Fail fetch() calls that require preflight and also redirect. r=sicking a=abillings (c0d6742b9e)
- Bug 1193128 - Fix base64 decoding when fetching data URIs. r=baku (80bafa291a)
- Bug 1195167 part 1: Let necko handle all protocols. r=bkelly (bb932b0ada)
- Bug 1195167 part 2: Remove redundant aCORSFlag argument and instead use mCORSFlagEverSet. r=bkelly (beadafcad0)
- Bug 1195167 part 3: Remove more scheme-specific handling from FetchDriver. r=bkelly (d00b38db9e)
- Bug 1195167 part 4: Remove FetchDriver::BasicFetch since it is empty. r=bkelly (c5ed097267)
- Bug 1210413 P2 Test CORS credentials on cross-origin redirects. r=sicking a=dveditz (b4eeb8aac0)
- Bug 1210413 P1 Propagate new channel load flags from child to parent on redirect. r=jduell a=dveditz (8b329af4fa)
- Bug 1195167 part 5: Make FetchDriver use AsyncOpen2. r=bkelly (cc217c4cc1)
- Bug 1195167 part 6: Some code simplification since necko handles fetch recursion. r=bkelly (f3b6da2262)
- Bug 1195167: Followup to fix test which I forgot to change (81e7439a2e)
- Bug 1215746: Remove RequestMode::Cors_with_forced_preflight. r=bkelly (0336e812b6)
- Bug 1211000: Move CORS preflight logic from nsCORSListenerProxy to nsCORSPreflightListener. r=ehsan (bf2f71cf22)
- missing bit of Bug 1211443 - Drop scheduled update if decoder initialization isn't done yet. r=jya (f6bc074e33)
- Bug 1182571: Fix nsILoadInfo->GetContentPolicyType API to be less ambigious. Audit and fix all users of it. r=ckerschb (5af6fa7442)
- fix (e40c8e7625)
- Bug 1173811 - Part 1: Propagate the response URL to intercepted channels when necessary (non-e10s). r=mayhemer,bkelly (26f4f13c28)
- Bug 1173811 - Part 2: Propagate the response URL to intercepted channels when necessary (e10s). r=mayhemer,bkelly (a603fe1df2)
- Bug 1154309 - Add New Resource Timing Fields r=bz,hurley (1d14eb6bef)
- Bug 1175685 - add OriginAttribute to LoadInfo. r=jonas, r=ckerschb, r=michal (a5d18bb637)
- Bug 1175685 - add OriginAttribute to LoadInfo. r=jonas, r=ckerschb, r=michal (fb07d2c8aa)
- Bug 1212904 P1 Add a LoadTainting enumeration. r=jduell (a1db8a3e99)
- Bug 1212904 P2 Add LoadTainting information to nsILoadInfo. r=jduell (2482e5e334)
- Bug 1221151 - use [infallible] in nsILoadInfo.idl instead of manual %{C++ blocks; r=jduell (aae73129b6)
- Bug 1045891 - CSP 2 child-src implementation r=ckerschb (792920aeb9)
- Bug 1219931 - CSP: Don't allow removing a policy (r=sicking) (9daaab4186)
- Bug 1208661 - Dump client-side layer textures. r=BenWa (1f2d17d515)
1619 lines
61 KiB
C++
1619 lines
61 KiB
C++
/* -*- Mode: C++; tab-width: 2; 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 "nsMathMLContainerFrame.h"
|
|
|
|
#include "gfxUtils.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsStyleContext.h"
|
|
#include "nsNameSpaceManager.h"
|
|
#include "nsRenderingContext.h"
|
|
#include "nsIDOMMutationEvent.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsAutoPtr.h"
|
|
#include "nsDisplayList.h"
|
|
#include "nsIReflowCallback.h"
|
|
#include "mozilla/Likely.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsMathMLElement.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::gfx;
|
|
|
|
//
|
|
// nsMathMLContainerFrame implementation
|
|
//
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLContainerFrame)
|
|
|
|
NS_QUERYFRAME_HEAD(nsMathMLContainerFrame)
|
|
NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
|
|
NS_QUERYFRAME_ENTRY(nsMathMLContainerFrame)
|
|
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
|
|
|
|
// =============================================================================
|
|
|
|
// error handlers
|
|
// provide a feedback to the user when a frame with bad markup can not be rendered
|
|
nsresult
|
|
nsMathMLContainerFrame::ReflowError(nsRenderingContext& aRenderingContext,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
// clear all other flags and record that there is an error with this frame
|
|
mEmbellishData.flags = 0;
|
|
mPresentationData.flags = NS_MATHML_ERROR;
|
|
|
|
///////////////
|
|
// Set font
|
|
RefPtr<nsFontMetrics> fm;
|
|
nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
|
|
nsLayoutUtils::
|
|
FontSizeInflationFor(this));
|
|
|
|
// bounding metrics
|
|
nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup");
|
|
mBoundingMetrics =
|
|
nsLayoutUtils::AppUnitBoundsOfString(errorMsg.get(), errorMsg.Length(),
|
|
*fm, aRenderingContext);
|
|
|
|
// reflow metrics
|
|
WritingMode wm = aDesiredSize.GetWritingMode();
|
|
aDesiredSize.SetBlockStartAscent(fm->MaxAscent());
|
|
nscoord descent = fm->MaxDescent();
|
|
aDesiredSize.BSize(wm) = aDesiredSize.BlockStartAscent() + descent;
|
|
aDesiredSize.ISize(wm) = mBoundingMetrics.width;
|
|
|
|
// Also return our bounding metrics
|
|
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
class nsDisplayMathMLError : public nsDisplayItem {
|
|
public:
|
|
nsDisplayMathMLError(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
|
|
: nsDisplayItem(aBuilder, aFrame) {
|
|
MOZ_COUNT_CTOR(nsDisplayMathMLError);
|
|
}
|
|
#ifdef NS_BUILD_REFCNT_LOGGING
|
|
virtual ~nsDisplayMathMLError() {
|
|
MOZ_COUNT_DTOR(nsDisplayMathMLError);
|
|
}
|
|
#endif
|
|
|
|
virtual void Paint(nsDisplayListBuilder* aBuilder,
|
|
nsRenderingContext* aCtx) override;
|
|
NS_DISPLAY_DECL_NAME("MathMLError", TYPE_MATHML_ERROR)
|
|
};
|
|
|
|
void nsDisplayMathMLError::Paint(nsDisplayListBuilder* aBuilder,
|
|
nsRenderingContext* aCtx)
|
|
{
|
|
// Set color and font ...
|
|
RefPtr<nsFontMetrics> fm;
|
|
nsLayoutUtils::GetFontMetricsForFrame(mFrame, getter_AddRefs(fm));
|
|
|
|
nsPoint pt = ToReferenceFrame();
|
|
int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
|
|
DrawTarget* drawTarget = aCtx->GetDrawTarget();
|
|
Rect rect = NSRectToSnappedRect(nsRect(pt, mFrame->GetSize()),
|
|
appUnitsPerDevPixel,
|
|
*drawTarget);
|
|
ColorPattern red(ToDeviceColor(Color(1.f, 0.f, 0.f, 1.f)));
|
|
drawTarget->FillRect(rect, red);
|
|
|
|
aCtx->ThebesContext()->SetColor(Color(1.f, 1.f, 1.f));
|
|
nscoord ascent = fm->MaxAscent();
|
|
NS_NAMED_LITERAL_STRING(errorMsg, "invalid-markup");
|
|
nsLayoutUtils::DrawUniDirString(errorMsg.get(), uint32_t(errorMsg.Length()),
|
|
nsPoint(pt.x, pt.y + ascent), *fm, *aCtx);
|
|
}
|
|
|
|
/* /////////////
|
|
* nsIMathMLFrame - support methods for stretchy elements
|
|
* =============================================================================
|
|
*/
|
|
|
|
static bool
|
|
IsForeignChild(const nsIFrame* aFrame)
|
|
{
|
|
// This counts nsMathMLmathBlockFrame as a foreign child, because it
|
|
// uses block reflow
|
|
return !(aFrame->IsFrameOfType(nsIFrame::eMathML)) ||
|
|
aFrame->GetType() == nsGkAtoms::blockFrame;
|
|
}
|
|
|
|
NS_DECLARE_FRAME_PROPERTY(HTMLReflowMetricsProperty,
|
|
DeleteValue<nsHTMLReflowMetrics>)
|
|
|
|
/* static */ void
|
|
nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(nsIFrame* aFrame,
|
|
const nsHTMLReflowMetrics& aReflowMetrics,
|
|
const nsBoundingMetrics& aBoundingMetrics)
|
|
{
|
|
nsHTMLReflowMetrics *metrics = new nsHTMLReflowMetrics(aReflowMetrics);
|
|
metrics->mBoundingMetrics = aBoundingMetrics;
|
|
aFrame->Properties().Set(HTMLReflowMetricsProperty(), metrics);
|
|
}
|
|
|
|
// helper method to facilitate getting the reflow and bounding metrics
|
|
/* static */ void
|
|
nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame* aFrame,
|
|
nsHTMLReflowMetrics& aReflowMetrics,
|
|
nsBoundingMetrics& aBoundingMetrics,
|
|
eMathMLFrameType* aMathMLFrameType)
|
|
{
|
|
NS_PRECONDITION(aFrame, "null arg");
|
|
|
|
nsHTMLReflowMetrics *metrics = static_cast<nsHTMLReflowMetrics*>
|
|
(aFrame->Properties().Get(HTMLReflowMetricsProperty()));
|
|
|
|
// IMPORTANT: This function is only meant to be called in Place() methods
|
|
// where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
|
|
// information.
|
|
NS_ASSERTION(metrics, "Didn't SaveReflowAndBoundingMetricsFor frame!");
|
|
if (metrics) {
|
|
aReflowMetrics = *metrics;
|
|
aBoundingMetrics = metrics->mBoundingMetrics;
|
|
}
|
|
|
|
if (aMathMLFrameType) {
|
|
if (!IsForeignChild(aFrame)) {
|
|
nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
|
|
if (mathMLFrame) {
|
|
*aMathMLFrameType = mathMLFrame->GetMathMLFrameType();
|
|
return;
|
|
}
|
|
}
|
|
*aMathMLFrameType = eMathMLFrameType_UNKNOWN;
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
nsMathMLContainerFrame::ClearSavedChildMetrics()
|
|
{
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
FramePropertyTable* props = PresContext()->PropertyTable();
|
|
while (childFrame) {
|
|
props->Delete(childFrame, HTMLReflowMetricsProperty());
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
}
|
|
|
|
// helper to get the preferred size that a container frame should use to fire
|
|
// the stretch on its stretchy child frames.
|
|
void
|
|
nsMathMLContainerFrame::GetPreferredStretchSize(nsRenderingContext& aRenderingContext,
|
|
uint32_t aOptions,
|
|
nsStretchDirection aStretchDirection,
|
|
nsBoundingMetrics& aPreferredStretchSize)
|
|
{
|
|
if (aOptions & STRETCH_CONSIDER_ACTUAL_SIZE) {
|
|
// when our actual size is ok, just use it
|
|
aPreferredStretchSize = mBoundingMetrics;
|
|
}
|
|
else if (aOptions & STRETCH_CONSIDER_EMBELLISHMENTS) {
|
|
// compute our up-to-date size using Place()
|
|
nsHTMLReflowMetrics metrics(GetWritingMode()); // ???
|
|
Place(aRenderingContext, false, metrics);
|
|
aPreferredStretchSize = metrics.mBoundingMetrics;
|
|
}
|
|
else {
|
|
// compute a size that includes embellishments iff the container stretches
|
|
// in the same direction as the embellished operator.
|
|
bool stretchAll = aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL ?
|
|
NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) :
|
|
NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
|
|
NS_ASSERTION(aStretchDirection == NS_STRETCH_DIRECTION_HORIZONTAL ||
|
|
aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL,
|
|
"You must specify a direction in which to stretch");
|
|
NS_ASSERTION(NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
|
|
stretchAll,
|
|
"invalid call to GetPreferredStretchSize");
|
|
bool firstTime = true;
|
|
nsBoundingMetrics bm, bmChild;
|
|
nsIFrame* childFrame =
|
|
stretchAll ? GetFirstPrincipalChild() : mPresentationData.baseFrame;
|
|
while (childFrame) {
|
|
// initializations in case this child happens not to be a MathML frame
|
|
nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
|
|
if (mathMLFrame) {
|
|
nsEmbellishData embellishData;
|
|
nsPresentationData presentationData;
|
|
mathMLFrame->GetEmbellishData(embellishData);
|
|
mathMLFrame->GetPresentationData(presentationData);
|
|
if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags) &&
|
|
embellishData.direction == aStretchDirection &&
|
|
presentationData.baseFrame) {
|
|
// embellishements are not included, only consider the inner first child itself
|
|
// XXXkt Does that mean the core descendent frame should be used
|
|
// instead of the base child?
|
|
nsIMathMLFrame* mathMLchildFrame = do_QueryFrame(presentationData.baseFrame);
|
|
if (mathMLchildFrame) {
|
|
mathMLFrame = mathMLchildFrame;
|
|
}
|
|
}
|
|
mathMLFrame->GetBoundingMetrics(bmChild);
|
|
}
|
|
else {
|
|
nsHTMLReflowMetrics unused(GetWritingMode());
|
|
GetReflowAndBoundingMetricsFor(childFrame, unused, bmChild);
|
|
}
|
|
|
|
if (firstTime) {
|
|
firstTime = false;
|
|
bm = bmChild;
|
|
if (!stretchAll) {
|
|
// we may get here for cases such as <msup><mo>...</mo> ... </msup>,
|
|
// or <maction>...<mo>...</mo></maction>.
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (aStretchDirection == NS_STRETCH_DIRECTION_HORIZONTAL) {
|
|
// if we get here, it means this is container that will stack its children
|
|
// vertically and fire an horizontal stretch on each them. This is the case
|
|
// for \munder, \mover, \munderover. We just sum-up the size vertically.
|
|
bm.descent += bmChild.ascent + bmChild.descent;
|
|
// Sometimes non-spacing marks (when width is zero) are positioned
|
|
// to the left of the origin, but it is the distance between left
|
|
// and right bearing that is important rather than the offsets from
|
|
// the origin.
|
|
if (bmChild.width == 0) {
|
|
bmChild.rightBearing -= bmChild.leftBearing;
|
|
bmChild.leftBearing = 0;
|
|
}
|
|
if (bm.leftBearing > bmChild.leftBearing)
|
|
bm.leftBearing = bmChild.leftBearing;
|
|
if (bm.rightBearing < bmChild.rightBearing)
|
|
bm.rightBearing = bmChild.rightBearing;
|
|
}
|
|
else if (aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) {
|
|
// just sum-up the sizes horizontally.
|
|
bm += bmChild;
|
|
}
|
|
else {
|
|
NS_ERROR("unexpected case in GetPreferredStretchSize");
|
|
break;
|
|
}
|
|
}
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
aPreferredStretchSize = bm;
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::Stretch(nsRenderingContext& aRenderingContext,
|
|
nsStretchDirection aStretchDirection,
|
|
nsBoundingMetrics& aContainerSize,
|
|
nsHTMLReflowMetrics& aDesiredStretchSize)
|
|
{
|
|
if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
|
|
|
|
if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
|
|
NS_WARNING("it is wrong to fire stretch more than once on a frame");
|
|
return NS_OK;
|
|
}
|
|
mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
|
|
|
|
if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
|
|
NS_WARNING("it is wrong to fire stretch on a erroneous frame");
|
|
return NS_OK;
|
|
}
|
|
|
|
// Pass the stretch to the base child ...
|
|
|
|
nsIFrame* baseFrame = mPresentationData.baseFrame;
|
|
if (baseFrame) {
|
|
nsIMathMLFrame* mathMLFrame = do_QueryFrame(baseFrame);
|
|
NS_ASSERTION(mathMLFrame, "Something is wrong somewhere");
|
|
if (mathMLFrame) {
|
|
|
|
// And the trick is that the child's rect.x is still holding the descent,
|
|
// and rect.y is still holding the ascent ...
|
|
nsHTMLReflowMetrics childSize(aDesiredStretchSize);
|
|
GetReflowAndBoundingMetricsFor(baseFrame, childSize, childSize.mBoundingMetrics);
|
|
|
|
// See if we should downsize and confine the stretch to us...
|
|
// XXX there may be other cases where we can downsize the stretch,
|
|
// e.g., the first ∑ might appear big in the following situation
|
|
// <math xmlns='http://www.w3.org/1998/Math/MathML'>
|
|
// <mstyle>
|
|
// <msub>
|
|
// <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
|
|
// <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
|
|
// </msub>
|
|
// </mstyle>
|
|
// </math>
|
|
nsBoundingMetrics containerSize = aContainerSize;
|
|
if (aStretchDirection != mEmbellishData.direction &&
|
|
mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED) {
|
|
NS_ASSERTION(mEmbellishData.direction != NS_STRETCH_DIRECTION_DEFAULT,
|
|
"Stretches may have a default direction, operators can not.");
|
|
if (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL ?
|
|
NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) :
|
|
NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) {
|
|
GetPreferredStretchSize(aRenderingContext, 0,
|
|
mEmbellishData.direction, containerSize);
|
|
// Stop further recalculations
|
|
aStretchDirection = mEmbellishData.direction;
|
|
} else {
|
|
// We aren't going to stretch the child, so just use the child metrics.
|
|
containerSize = childSize.mBoundingMetrics;
|
|
}
|
|
}
|
|
|
|
// do the stretching...
|
|
mathMLFrame->Stretch(aRenderingContext,
|
|
aStretchDirection, containerSize, childSize);
|
|
// store the updated metrics
|
|
SaveReflowAndBoundingMetricsFor(baseFrame, childSize,
|
|
childSize.mBoundingMetrics);
|
|
|
|
// Remember the siblings which were _deferred_.
|
|
// Now that this embellished child may have changed, we need to
|
|
// fire the stretch on its siblings using our updated size
|
|
|
|
if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
|
|
NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) {
|
|
|
|
nsStretchDirection stretchDir =
|
|
NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ?
|
|
NS_STRETCH_DIRECTION_VERTICAL : NS_STRETCH_DIRECTION_HORIZONTAL;
|
|
|
|
GetPreferredStretchSize(aRenderingContext, STRETCH_CONSIDER_EMBELLISHMENTS,
|
|
stretchDir, containerSize);
|
|
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
if (childFrame != mPresentationData.baseFrame) {
|
|
mathMLFrame = do_QueryFrame(childFrame);
|
|
if (mathMLFrame) {
|
|
// retrieve the metrics that was stored at the previous pass
|
|
GetReflowAndBoundingMetricsFor(childFrame,
|
|
childSize, childSize.mBoundingMetrics);
|
|
// do the stretching...
|
|
mathMLFrame->Stretch(aRenderingContext, stretchDir,
|
|
containerSize, childSize);
|
|
// store the updated metrics
|
|
SaveReflowAndBoundingMetricsFor(childFrame, childSize,
|
|
childSize.mBoundingMetrics);
|
|
}
|
|
}
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
}
|
|
|
|
// re-position all our children
|
|
nsresult rv = Place(aRenderingContext, true, aDesiredStretchSize);
|
|
if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
|
|
// Make sure the child frames get their DidReflow() calls.
|
|
DidReflowChildren(mFrames.FirstChild());
|
|
}
|
|
|
|
// If our parent is not embellished, it means we are the outermost embellished
|
|
// container and so we put the spacing, otherwise we don't include the spacing,
|
|
// the outermost embellished container will take care of it.
|
|
|
|
nsEmbellishData parentData;
|
|
GetEmbellishDataFrom(GetParent(), parentData);
|
|
// ensure that we are the embellished child, not just a sibling
|
|
// (need to test coreFrame since <mfrac> resets other things)
|
|
if (parentData.coreFrame != mEmbellishData.coreFrame) {
|
|
// (we fetch values from the core since they may use units that depend
|
|
// on style data, and style changes could have occurred in the core since
|
|
// our last visit there)
|
|
nsEmbellishData coreData;
|
|
GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
|
|
|
|
mBoundingMetrics.width +=
|
|
coreData.leadingSpace + coreData.trailingSpace;
|
|
aDesiredStretchSize.Width() = mBoundingMetrics.width;
|
|
aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
|
|
|
|
nscoord dx = (StyleVisibility()->mDirection ?
|
|
coreData.trailingSpace : coreData.leadingSpace);
|
|
if (dx != 0) {
|
|
mBoundingMetrics.leftBearing += dx;
|
|
mBoundingMetrics.rightBearing += dx;
|
|
aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
|
|
aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
|
|
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
childFrame->SetPosition(childFrame->GetPosition()
|
|
+ nsPoint(dx, 0));
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Finished with these:
|
|
ClearSavedChildMetrics();
|
|
// Set our overflow area
|
|
GatherAndStoreOverflow(&aDesiredStretchSize);
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsMathMLContainerFrame::FinalizeReflow(nsRenderingContext& aRenderingContext,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
// During reflow, we use rect.x and rect.y as placeholders for the child's ascent
|
|
// and descent in expectation of a stretch command. Hence we need to ensure that
|
|
// a stretch command will actually be fired later on, after exiting from our
|
|
// reflow. If the stretch is not fired, the rect.x, and rect.y will remain
|
|
// with inappropriate data causing children to be improperly positioned.
|
|
// This helper method checks to see if our parent will fire a stretch command
|
|
// targeted at us. If not, we go ahead and fire an involutive stretch on
|
|
// ourselves. This will clear all the rect.x and rect.y, and return our
|
|
// desired size.
|
|
|
|
|
|
// First, complete the post-reflow hook.
|
|
// We use the information in our children rectangles to position them.
|
|
// If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
|
|
// They will still be holding the ascent and descent for each child.
|
|
|
|
// The first clause caters for any non-embellished container.
|
|
// The second clause is for a container which won't fire stretch even though it is
|
|
// embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test is convoluted
|
|
// because it excludes the particular case of the core <mo>...</mo> itself.
|
|
// (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
|
|
bool placeOrigin = !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
|
|
(mEmbellishData.coreFrame != this && !mPresentationData.baseFrame &&
|
|
mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED);
|
|
nsresult rv = Place(aRenderingContext, placeOrigin, aDesiredSize);
|
|
|
|
// Place() will call FinishReflowChild() when placeOrigin is true but if
|
|
// it returns before reaching FinishReflowChild() due to errors we need
|
|
// to fulfill the reflow protocol by calling DidReflow for the child frames
|
|
// that still needs it here (or we may crash - bug 366012).
|
|
// If placeOrigin is false we should reach Place() with aPlaceOrigin == true
|
|
// through Stretch() eventually.
|
|
if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
|
|
GatherAndStoreOverflow(&aDesiredSize);
|
|
DidReflowChildren(GetFirstPrincipalChild());
|
|
return rv;
|
|
}
|
|
|
|
bool parentWillFireStretch = false;
|
|
if (!placeOrigin) {
|
|
// This means the rect.x and rect.y of our children were not set!!
|
|
// Don't go without checking to see if our parent will later fire a Stretch() command
|
|
// targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
|
|
nsIMathMLFrame* mathMLFrame = do_QueryFrame(GetParent());
|
|
if (mathMLFrame) {
|
|
nsEmbellishData embellishData;
|
|
nsPresentationData presentationData;
|
|
mathMLFrame->GetEmbellishData(embellishData);
|
|
mathMLFrame->GetPresentationData(presentationData);
|
|
if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(presentationData.flags) ||
|
|
NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(presentationData.flags) ||
|
|
(NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)
|
|
&& presentationData.baseFrame == this))
|
|
{
|
|
parentWillFireStretch = true;
|
|
}
|
|
}
|
|
if (!parentWillFireStretch) {
|
|
// There is nobody who will fire the stretch for us, we do it ourselves!
|
|
|
|
bool stretchAll =
|
|
/* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || */
|
|
NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
|
|
|
|
nsStretchDirection stretchDir;
|
|
if (mEmbellishData.coreFrame == this || /* case of a bare <mo>...</mo> itself */
|
|
(mEmbellishData.direction == NS_STRETCH_DIRECTION_HORIZONTAL &&
|
|
stretchAll) || /* or <mover><mo>...</mo>...</mover>, or friends */
|
|
mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED) { /* Doesn't stretch */
|
|
stretchDir = mEmbellishData.direction;
|
|
} else {
|
|
// Let the Stretch() call decide the direction.
|
|
stretchDir = NS_STRETCH_DIRECTION_DEFAULT;
|
|
}
|
|
// Use our current size as computed earlier by Place()
|
|
// The stretch call will detect if this is incorrect and recalculate the size.
|
|
nsBoundingMetrics defaultSize = aDesiredSize.mBoundingMetrics;
|
|
|
|
Stretch(aRenderingContext, stretchDir, defaultSize,
|
|
aDesiredSize);
|
|
#ifdef DEBUG
|
|
{
|
|
// The Place() call above didn't request FinishReflowChild(),
|
|
// so let's check that we eventually did through Stretch().
|
|
nsIFrame* childFrame = GetFirstPrincipalChild();
|
|
for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
|
|
NS_ASSERTION(!(childFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
|
|
"DidReflow() was never called");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Also return our bounding metrics
|
|
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
|
|
|
|
// see if we should fix the spacing
|
|
FixInterFrameSpacing(aDesiredSize);
|
|
|
|
if (!parentWillFireStretch) {
|
|
// Not expecting a stretch.
|
|
// Finished with these:
|
|
ClearSavedChildMetrics();
|
|
// Set our overflow area.
|
|
GatherAndStoreOverflow(&aDesiredSize);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
/* /////////////
|
|
* nsIMathMLFrame - support methods for scripting elements (nested frames
|
|
* within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
|
|
* mfrac, mroot, mtable).
|
|
* =============================================================================
|
|
*/
|
|
|
|
// helper to let the update of presentation data pass through
|
|
// a subtree that may contain non-mathml container frames
|
|
/* static */ void
|
|
nsMathMLContainerFrame::PropagatePresentationDataFor(nsIFrame* aFrame,
|
|
uint32_t aFlagsValues,
|
|
uint32_t aFlagsToUpdate)
|
|
{
|
|
if (!aFrame || !aFlagsToUpdate)
|
|
return;
|
|
nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
|
|
if (mathMLFrame) {
|
|
// update
|
|
mathMLFrame->UpdatePresentationData(aFlagsValues,
|
|
aFlagsToUpdate);
|
|
// propagate using the base method to make sure that the control
|
|
// is passed on to MathML frames that may be overloading the method
|
|
mathMLFrame->UpdatePresentationDataFromChildAt(0, -1,
|
|
aFlagsValues, aFlagsToUpdate);
|
|
}
|
|
else {
|
|
// propagate down the subtrees
|
|
nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
|
|
while (childFrame) {
|
|
PropagatePresentationDataFor(childFrame,
|
|
aFlagsValues, aFlagsToUpdate);
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(nsIFrame* aParentFrame,
|
|
int32_t aFirstChildIndex,
|
|
int32_t aLastChildIndex,
|
|
uint32_t aFlagsValues,
|
|
uint32_t aFlagsToUpdate)
|
|
{
|
|
if (!aParentFrame || !aFlagsToUpdate)
|
|
return;
|
|
int32_t index = 0;
|
|
nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
|
|
while (childFrame) {
|
|
if ((index >= aFirstChildIndex) &&
|
|
((aLastChildIndex <= 0) || ((aLastChildIndex > 0) &&
|
|
(index <= aLastChildIndex)))) {
|
|
PropagatePresentationDataFor(childFrame,
|
|
aFlagsValues, aFlagsToUpdate);
|
|
}
|
|
index++;
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
}
|
|
|
|
/* //////////////////
|
|
* Frame construction
|
|
* =============================================================================
|
|
*/
|
|
|
|
|
|
void
|
|
nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|
const nsRect& aDirtyRect,
|
|
const nsDisplayListSet& aLists)
|
|
{
|
|
// report an error if something wrong was found in this frame
|
|
if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
|
|
if (!IsVisibleForPainting(aBuilder))
|
|
return;
|
|
|
|
aLists.Content()->AppendNewToTop(
|
|
new (aBuilder) nsDisplayMathMLError(aBuilder, this));
|
|
return;
|
|
}
|
|
|
|
DisplayBorderBackgroundOutline(aBuilder, aLists);
|
|
|
|
BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists,
|
|
DISPLAY_CHILD_INLINE);
|
|
|
|
#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
|
|
// for visual debug
|
|
// ----------------
|
|
// if you want to see your bounding box, make sure to properly fill
|
|
// your mBoundingMetrics and mReference point, and set
|
|
// mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
|
|
// in the Init() of your sub-class
|
|
DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
|
|
#endif
|
|
}
|
|
|
|
// Note that this method re-builds the automatic data in the children -- not
|
|
// in aParentFrame itself (except for those particular operations that the
|
|
// parent frame may do in its TransmitAutomaticData()).
|
|
/* static */ void
|
|
nsMathMLContainerFrame::RebuildAutomaticDataForChildren(nsIFrame* aParentFrame)
|
|
{
|
|
// 1. As we descend the tree, make each child frame inherit data from
|
|
// the parent
|
|
// 2. As we ascend the tree, transmit any specific change that we want
|
|
// down the subtrees
|
|
nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
|
|
while (childFrame) {
|
|
nsIMathMLFrame* childMathMLFrame = do_QueryFrame(childFrame);
|
|
if (childMathMLFrame) {
|
|
childMathMLFrame->InheritAutomaticData(aParentFrame);
|
|
}
|
|
RebuildAutomaticDataForChildren(childFrame);
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
nsIMathMLFrame* mathMLFrame = do_QueryFrame(aParentFrame);
|
|
if (mathMLFrame) {
|
|
mathMLFrame->TransmitAutomaticData();
|
|
}
|
|
}
|
|
|
|
/* static */ nsresult
|
|
nsMathMLContainerFrame::ReLayoutChildren(nsIFrame* aParentFrame)
|
|
{
|
|
if (!aParentFrame)
|
|
return NS_OK;
|
|
|
|
// walk-up to the first frame that is a MathML frame, stop if we reach <math>
|
|
nsIFrame* frame = aParentFrame;
|
|
while (1) {
|
|
nsIFrame* parent = frame->GetParent();
|
|
if (!parent || !parent->GetContent())
|
|
break;
|
|
|
|
// stop if it is a MathML frame
|
|
nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
|
|
if (mathMLFrame)
|
|
break;
|
|
|
|
// stop if we reach the root <math> tag
|
|
nsIContent* content = frame->GetContent();
|
|
NS_ASSERTION(content, "dangling frame without a content node");
|
|
if (!content)
|
|
break;
|
|
if (content->IsMathMLElement(nsGkAtoms::math))
|
|
break;
|
|
|
|
frame = parent;
|
|
}
|
|
|
|
// re-sync the presentation data and embellishment data of our children
|
|
RebuildAutomaticDataForChildren(frame);
|
|
|
|
// Ask our parent frame to reflow us
|
|
nsIFrame* parent = frame->GetParent();
|
|
NS_ASSERTION(parent, "No parent to pass the reflow request up to");
|
|
if (!parent)
|
|
return NS_OK;
|
|
|
|
frame->PresContext()->PresShell()->
|
|
FrameNeedsReflow(frame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// There are precise rules governing children of a MathML frame,
|
|
// and properties such as the scriptlevel depends on those rules.
|
|
// Hence for things to work, callers must use Append/Insert/etc wisely.
|
|
|
|
nsresult
|
|
nsMathMLContainerFrame::ChildListChanged(int32_t aModType)
|
|
{
|
|
// If this is an embellished frame we need to rebuild the
|
|
// embellished hierarchy by walking-up to the parent of the
|
|
// outermost embellished container.
|
|
nsIFrame* frame = this;
|
|
if (mEmbellishData.coreFrame) {
|
|
nsIFrame* parent = GetParent();
|
|
nsEmbellishData embellishData;
|
|
for ( ; parent; frame = parent, parent = parent->GetParent()) {
|
|
GetEmbellishDataFrom(parent, embellishData);
|
|
if (embellishData.coreFrame != mEmbellishData.coreFrame)
|
|
break;
|
|
}
|
|
}
|
|
return ReLayoutChildren(frame);
|
|
}
|
|
|
|
void
|
|
nsMathMLContainerFrame::AppendFrames(ChildListID aListID,
|
|
nsFrameList& aFrameList)
|
|
{
|
|
MOZ_ASSERT(aListID == kPrincipalList);
|
|
mFrames.AppendFrames(this, aFrameList);
|
|
ChildListChanged(nsIDOMMutationEvent::ADDITION);
|
|
}
|
|
|
|
void
|
|
nsMathMLContainerFrame::InsertFrames(ChildListID aListID,
|
|
nsIFrame* aPrevFrame,
|
|
nsFrameList& aFrameList)
|
|
{
|
|
MOZ_ASSERT(aListID == kPrincipalList);
|
|
mFrames.InsertFrames(this, aPrevFrame, aFrameList);
|
|
ChildListChanged(nsIDOMMutationEvent::ADDITION);
|
|
}
|
|
|
|
void
|
|
nsMathMLContainerFrame::RemoveFrame(ChildListID aListID,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
MOZ_ASSERT(aListID == kPrincipalList);
|
|
mFrames.DestroyFrame(aOldFrame);
|
|
ChildListChanged(nsIDOMMutationEvent::REMOVAL);
|
|
}
|
|
|
|
nsresult
|
|
nsMathMLContainerFrame::AttributeChanged(int32_t aNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
int32_t aModType)
|
|
{
|
|
// XXX Since they are numerous MathML attributes that affect layout, and
|
|
// we can't check all of them here, play safe by requesting a reflow.
|
|
// XXXldb This should only do work for attributes that cause changes!
|
|
PresContext()->PresShell()->
|
|
FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsMathMLContainerFrame::GatherAndStoreOverflow(nsHTMLReflowMetrics* aMetrics)
|
|
{
|
|
// nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
|
|
// frame rectangle.
|
|
aMetrics->SetOverflowAreasToDesiredBounds();
|
|
|
|
// All non-child-frame content such as nsMathMLChars (and most child-frame
|
|
// content) is included in mBoundingMetrics.
|
|
nsRect boundingBox(mBoundingMetrics.leftBearing,
|
|
aMetrics->BlockStartAscent() - mBoundingMetrics.ascent,
|
|
mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing,
|
|
mBoundingMetrics.ascent + mBoundingMetrics.descent);
|
|
|
|
// REVIEW: Maybe this should contribute only to visual overflow
|
|
// and not scrollable?
|
|
aMetrics->mOverflowAreas.UnionAllWith(boundingBox);
|
|
|
|
// mBoundingMetrics does not necessarily include content of <mpadded>
|
|
// elements whose mBoundingMetrics may not be representative of the true
|
|
// bounds, and doesn't include the CSS2 outline rectangles of children, so
|
|
// make such to include child overflow areas.
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
ConsiderChildOverflow(aMetrics->mOverflowAreas, childFrame);
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
|
|
FinishAndStoreOverflow(aMetrics);
|
|
}
|
|
|
|
bool
|
|
nsMathMLContainerFrame::UpdateOverflow()
|
|
{
|
|
// Our overflow areas may have changed, so reflow the frame.
|
|
PresContext()->PresShell()->FrameNeedsReflow(
|
|
this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
|
|
|
|
// As we're reflowing, there's no need to propagate this change.
|
|
return false;
|
|
}
|
|
|
|
void
|
|
nsMathMLContainerFrame::ReflowChild(nsIFrame* aChildFrame,
|
|
nsPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
// Having foreign/hybrid children, e.g., from html markups, is not defined by
|
|
// the MathML spec. But it can happen in practice, e.g., <html:img> allows us
|
|
// to do some cool demos... or we may have a child that is an nsInlineFrame
|
|
// from a generated content such as :before { content: open-quote } or
|
|
// :after { content: close-quote }. Unfortunately, the other frames out-there
|
|
// may expect their own invariants that are not met when we mix things.
|
|
// Hence we do not claim their support, but we will nevertheless attempt to keep
|
|
// them in the flow, if we can get their desired size. We observed that most
|
|
// frames may be reflowed generically, but nsInlineFrames need extra care.
|
|
|
|
#ifdef DEBUG
|
|
nsInlineFrame* inlineFrame = do_QueryFrame(aChildFrame);
|
|
NS_ASSERTION(!inlineFrame, "Inline frames should be wrapped in blocks");
|
|
#endif
|
|
|
|
nsContainerFrame::
|
|
ReflowChild(aChildFrame, aPresContext, aDesiredSize, aReflowState,
|
|
0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
|
|
|
|
if (aDesiredSize.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
|
|
// This will be suitable for inline frames, which are wrapped in a block.
|
|
nscoord ascent;
|
|
WritingMode wm = aDesiredSize.GetWritingMode();
|
|
if (!nsLayoutUtils::GetLastLineBaseline(wm, aChildFrame, &ascent)) {
|
|
// We don't expect any other block children so just place the frame on
|
|
// the baseline instead of going through DidReflow() and
|
|
// GetBaseline(). This is what nsFrame::GetBaseline() will do anyway.
|
|
aDesiredSize.SetBlockStartAscent(aDesiredSize.BSize(wm));
|
|
} else {
|
|
aDesiredSize.SetBlockStartAscent(ascent);
|
|
}
|
|
}
|
|
if (IsForeignChild(aChildFrame)) {
|
|
// use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set.
|
|
nsRect r = aChildFrame->ComputeTightBounds(aReflowState.rendContext->ThebesContext());
|
|
aDesiredSize.mBoundingMetrics.leftBearing = r.x;
|
|
aDesiredSize.mBoundingMetrics.rightBearing = r.XMost();
|
|
aDesiredSize.mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent() - r.y;
|
|
aDesiredSize.mBoundingMetrics.descent = r.YMost() - aDesiredSize.BlockStartAscent();
|
|
aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
|
|
}
|
|
}
|
|
|
|
void
|
|
nsMathMLContainerFrame::Reflow(nsPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
MarkInReflow();
|
|
mPresentationData.flags &= ~NS_MATHML_ERROR;
|
|
aDesiredSize.Width() = aDesiredSize.Height() = 0;
|
|
aDesiredSize.SetBlockStartAscent(0);
|
|
aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
|
|
|
|
/////////////
|
|
// Reflow children
|
|
// Asking each child to cache its bounding metrics
|
|
|
|
nsReflowStatus childStatus;
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
nsHTMLReflowMetrics childDesiredSize(aReflowState, // ???
|
|
aDesiredSize.mFlags);
|
|
WritingMode wm = childFrame->GetWritingMode();
|
|
LogicalSize availSize = aReflowState.ComputedSize(wm);
|
|
availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
|
|
nsHTMLReflowState childReflowState(aPresContext, aReflowState,
|
|
childFrame, availSize);
|
|
ReflowChild(childFrame, aPresContext, childDesiredSize,
|
|
childReflowState, childStatus);
|
|
//NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
|
|
SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
|
|
childDesiredSize.mBoundingMetrics);
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
|
|
/////////////
|
|
// If we are a container which is entitled to stretch its children, then we
|
|
// ask our stretchy children to stretch themselves
|
|
|
|
// The stretching of siblings of an embellished child is _deferred_ until
|
|
// after finishing the stretching of the embellished child - bug 117652
|
|
|
|
if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) &&
|
|
(NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
|
|
NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags))) {
|
|
|
|
// get the stretchy direction
|
|
nsStretchDirection stretchDir =
|
|
NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)
|
|
? NS_STRETCH_DIRECTION_VERTICAL
|
|
: NS_STRETCH_DIRECTION_HORIZONTAL;
|
|
|
|
// what size should we use to stretch our stretchy children
|
|
// We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not known yet
|
|
// We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we don't want to
|
|
// include them in the caculations of the size of stretchy elements
|
|
nsBoundingMetrics containerSize;
|
|
GetPreferredStretchSize(*aReflowState.rendContext, 0, stretchDir,
|
|
containerSize);
|
|
|
|
// fire the stretch on each child
|
|
childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
|
|
if (mathMLFrame) {
|
|
// retrieve the metrics that was stored at the previous pass
|
|
nsHTMLReflowMetrics childDesiredSize(aReflowState);
|
|
GetReflowAndBoundingMetricsFor(childFrame,
|
|
childDesiredSize, childDesiredSize.mBoundingMetrics);
|
|
|
|
mathMLFrame->Stretch(*aReflowState.rendContext, stretchDir,
|
|
containerSize, childDesiredSize);
|
|
// store the updated metrics
|
|
SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
|
|
childDesiredSize.mBoundingMetrics);
|
|
}
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
}
|
|
|
|
/////////////
|
|
// Place children now by re-adjusting the origins to align the baselines
|
|
FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
|
|
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
|
|
}
|
|
|
|
static nscoord AddInterFrameSpacingToSize(nsHTMLReflowMetrics& aDesiredSize,
|
|
nsMathMLContainerFrame* aFrame);
|
|
|
|
/* virtual */ nscoord
|
|
nsMathMLContainerFrame::GetMinISize(nsRenderingContext *aRenderingContext)
|
|
{
|
|
nscoord result;
|
|
DISPLAY_MIN_WIDTH(this, result);
|
|
nsHTMLReflowMetrics desiredSize(GetWritingMode());
|
|
GetIntrinsicISizeMetrics(aRenderingContext, desiredSize);
|
|
|
|
// Include the additional width added by FixInterFrameSpacing to ensure
|
|
// consistent width calculations.
|
|
AddInterFrameSpacingToSize(desiredSize, this);
|
|
result = desiredSize.ISize(GetWritingMode());
|
|
return result;
|
|
}
|
|
|
|
/* virtual */ nscoord
|
|
nsMathMLContainerFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
|
|
{
|
|
nscoord result;
|
|
DISPLAY_PREF_WIDTH(this, result);
|
|
nsHTMLReflowMetrics desiredSize(GetWritingMode());
|
|
GetIntrinsicISizeMetrics(aRenderingContext, desiredSize);
|
|
|
|
// Include the additional width added by FixInterFrameSpacing to ensure
|
|
// consistent width calculations.
|
|
AddInterFrameSpacingToSize(desiredSize, this);
|
|
result = desiredSize.ISize(GetWritingMode());
|
|
return result;
|
|
}
|
|
|
|
/* virtual */ void
|
|
nsMathMLContainerFrame::GetIntrinsicISizeMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
// Get child widths
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
nsHTMLReflowMetrics childDesiredSize(GetWritingMode()); // ???
|
|
|
|
nsMathMLContainerFrame* containerFrame = do_QueryFrame(childFrame);
|
|
if (containerFrame) {
|
|
containerFrame->GetIntrinsicISizeMetrics(aRenderingContext,
|
|
childDesiredSize);
|
|
} else {
|
|
// XXX This includes margin while Reflow currently doesn't consider
|
|
// margin, so we may end up with too much space, but, with stretchy
|
|
// characters, this is an approximation anyway.
|
|
nscoord width =
|
|
nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
|
|
nsLayoutUtils::PREF_ISIZE);
|
|
|
|
childDesiredSize.Width() = width;
|
|
childDesiredSize.mBoundingMetrics.width = width;
|
|
childDesiredSize.mBoundingMetrics.leftBearing = 0;
|
|
childDesiredSize.mBoundingMetrics.rightBearing = width;
|
|
|
|
nscoord x, xMost;
|
|
if (NS_SUCCEEDED(childFrame->GetPrefWidthTightBounds(aRenderingContext,
|
|
&x, &xMost))) {
|
|
childDesiredSize.mBoundingMetrics.leftBearing = x;
|
|
childDesiredSize.mBoundingMetrics.rightBearing = xMost;
|
|
}
|
|
}
|
|
|
|
SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
|
|
childDesiredSize.mBoundingMetrics);
|
|
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
|
|
// Measure
|
|
nsresult rv = MeasureForWidth(*aRenderingContext, aDesiredSize);
|
|
if (NS_FAILED(rv)) {
|
|
ReflowError(*aRenderingContext, aDesiredSize);
|
|
}
|
|
|
|
ClearSavedChildMetrics();
|
|
}
|
|
|
|
/* virtual */ nsresult
|
|
nsMathMLContainerFrame::MeasureForWidth(nsRenderingContext& aRenderingContext,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
return Place(aRenderingContext, false, aDesiredSize);
|
|
}
|
|
|
|
|
|
// see spacing table in Chapter 18, TeXBook (p.170)
|
|
// Our table isn't quite identical to TeX because operators have
|
|
// built-in values for lspace & rspace in the Operator Dictionary.
|
|
static int32_t kInterFrameSpacingTable[eMathMLFrameType_COUNT][eMathMLFrameType_COUNT] =
|
|
{
|
|
// in units of muspace.
|
|
// upper half of the byte is set if the
|
|
// spacing is not to be used for scriptlevel > 0
|
|
|
|
/* Ord OpOrd OpInv OpUsr Inner Italic Upright */
|
|
/*Ord */ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
|
|
/*OpOrd*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
/*OpInv*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
/*OpUsr*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
|
|
/*Inner*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
|
|
/*Italic*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
|
|
/*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00}
|
|
};
|
|
|
|
#define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_) \
|
|
/* no space if there is a frame that we know nothing about */ \
|
|
if (frametype1_ == eMathMLFrameType_UNKNOWN || \
|
|
frametype2_ == eMathMLFrameType_UNKNOWN) \
|
|
space_ = 0; \
|
|
else { \
|
|
space_ = kInterFrameSpacingTable[frametype1_][frametype2_]; \
|
|
space_ = (scriptlevel_ > 0 && (space_ & 0xF0)) \
|
|
? 0 /* spacing is disabled */ \
|
|
: space_ & 0x0F; \
|
|
} \
|
|
|
|
// This function computes the inter-space between two frames. However,
|
|
// since invisible operators need special treatment, the inter-space may
|
|
// be delayed when an invisible operator is encountered. In this case,
|
|
// the function will carry the inter-space forward until it is determined
|
|
// that it can be applied properly (i.e., until we encounter a visible
|
|
// frame where to decide whether to accept or reject the inter-space).
|
|
// aFromFrameType: remembers the frame when the carry-forward initiated.
|
|
// aCarrySpace: keeps track of the inter-space that is delayed.
|
|
// @returns: current inter-space (which is 0 when the true inter-space is
|
|
// delayed -- and thus has no effect since the frame is invisible anyway).
|
|
static nscoord
|
|
GetInterFrameSpacing(int32_t aScriptLevel,
|
|
eMathMLFrameType aFirstFrameType,
|
|
eMathMLFrameType aSecondFrameType,
|
|
eMathMLFrameType* aFromFrameType, // IN/OUT
|
|
int32_t* aCarrySpace) // IN/OUT
|
|
{
|
|
eMathMLFrameType firstType = aFirstFrameType;
|
|
eMathMLFrameType secondType = aSecondFrameType;
|
|
|
|
int32_t space;
|
|
GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
|
|
|
|
// feedback control to avoid the inter-space to be added when not necessary
|
|
if (secondType == eMathMLFrameType_OperatorInvisible) {
|
|
// see if we should start to carry the space forward until we
|
|
// encounter a visible frame
|
|
if (*aFromFrameType == eMathMLFrameType_UNKNOWN) {
|
|
*aFromFrameType = firstType;
|
|
*aCarrySpace = space;
|
|
}
|
|
// keep carrying *aCarrySpace forward, while returning 0 for this stage
|
|
space = 0;
|
|
}
|
|
else if (*aFromFrameType != eMathMLFrameType_UNKNOWN) {
|
|
// no carry-forward anymore, get the real inter-space between
|
|
// the two frames of interest
|
|
|
|
firstType = *aFromFrameType;
|
|
|
|
// But... the invisible operator that we encountered earlier could
|
|
// be sitting between italic and upright identifiers, e.g.,
|
|
//
|
|
// 1. <mi>sin</mi> <mo>⁡</mo> <mi>x</mi>
|
|
// 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
|
|
//
|
|
// the trick to get the inter-space in either situation
|
|
// is to promote "<mi>sin</mi><mo>⁡</mo>" and
|
|
// "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
|
|
if (firstType == eMathMLFrameType_UprightIdentifier) {
|
|
firstType = eMathMLFrameType_OperatorUserDefined;
|
|
}
|
|
else if (secondType == eMathMLFrameType_UprightIdentifier) {
|
|
secondType = eMathMLFrameType_OperatorUserDefined;
|
|
}
|
|
|
|
GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
|
|
|
|
// Now, we have two values: the computed space and the space that
|
|
// has been carried forward until now. Which value do we pick?
|
|
// If the second type is an operator (e.g., fence), it already has
|
|
// built-in lspace & rspace, so we let them win. Otherwise we pick
|
|
// the max between the two values that we have.
|
|
if (secondType != eMathMLFrameType_OperatorOrdinary &&
|
|
space < *aCarrySpace)
|
|
space = *aCarrySpace;
|
|
|
|
// reset everything now that the carry-forward is done
|
|
*aFromFrameType = eMathMLFrameType_UNKNOWN;
|
|
*aCarrySpace = 0;
|
|
}
|
|
|
|
return space;
|
|
}
|
|
|
|
static nscoord GetThinSpace(const nsStyleFont* aStyleFont)
|
|
{
|
|
return NSToCoordRound(float(aStyleFont->mFont.size)*float(3) / float(18));
|
|
}
|
|
|
|
class nsMathMLContainerFrame::RowChildFrameIterator {
|
|
public:
|
|
explicit RowChildFrameIterator(nsMathMLContainerFrame* aParentFrame) :
|
|
mParentFrame(aParentFrame),
|
|
mSize(aParentFrame->GetWritingMode()), // ???
|
|
mX(0),
|
|
mCarrySpace(0),
|
|
mFromFrameType(eMathMLFrameType_UNKNOWN),
|
|
mRTL(aParentFrame->StyleVisibility()->mDirection)
|
|
{
|
|
if (!mRTL) {
|
|
mChildFrame = aParentFrame->mFrames.FirstChild();
|
|
} else {
|
|
mChildFrame = aParentFrame->mFrames.LastChild();
|
|
}
|
|
|
|
if (!mChildFrame)
|
|
return;
|
|
|
|
InitMetricsForChild();
|
|
}
|
|
|
|
RowChildFrameIterator& operator++()
|
|
{
|
|
// add child size + italic correction
|
|
mX += mSize.mBoundingMetrics.width + mItalicCorrection;
|
|
|
|
if (!mRTL) {
|
|
mChildFrame = mChildFrame->GetNextSibling();
|
|
} else {
|
|
mChildFrame = mChildFrame->GetPrevSibling();
|
|
}
|
|
|
|
if (!mChildFrame)
|
|
return *this;
|
|
|
|
eMathMLFrameType prevFrameType = mChildFrameType;
|
|
InitMetricsForChild();
|
|
|
|
// add inter frame spacing
|
|
const nsStyleFont* font = mParentFrame->StyleFont();
|
|
nscoord space =
|
|
GetInterFrameSpacing(font->mScriptLevel,
|
|
prevFrameType, mChildFrameType,
|
|
&mFromFrameType, &mCarrySpace);
|
|
mX += space * GetThinSpace(font);
|
|
return *this;
|
|
}
|
|
|
|
nsIFrame* Frame() const { return mChildFrame; }
|
|
nscoord X() const { return mX; }
|
|
const nsHTMLReflowMetrics& ReflowMetrics() const { return mSize; }
|
|
nscoord Ascent() const { return mSize.BlockStartAscent(); }
|
|
nscoord Descent() const { return mSize.Height() - mSize.BlockStartAscent(); }
|
|
const nsBoundingMetrics& BoundingMetrics() const {
|
|
return mSize.mBoundingMetrics;
|
|
}
|
|
|
|
private:
|
|
const nsMathMLContainerFrame* mParentFrame;
|
|
nsIFrame* mChildFrame;
|
|
nsHTMLReflowMetrics mSize;
|
|
nscoord mX;
|
|
|
|
nscoord mItalicCorrection;
|
|
eMathMLFrameType mChildFrameType;
|
|
int32_t mCarrySpace;
|
|
eMathMLFrameType mFromFrameType;
|
|
|
|
bool mRTL;
|
|
|
|
void InitMetricsForChild()
|
|
{
|
|
GetReflowAndBoundingMetricsFor(mChildFrame, mSize, mSize.mBoundingMetrics,
|
|
&mChildFrameType);
|
|
nscoord leftCorrection, rightCorrection;
|
|
GetItalicCorrection(mSize.mBoundingMetrics,
|
|
leftCorrection, rightCorrection);
|
|
if (!mChildFrame->GetPrevSibling() &&
|
|
mParentFrame->GetContent()->IsMathMLElement(nsGkAtoms::msqrt_)) {
|
|
// Remove leading correction in <msqrt> because the sqrt glyph itself is
|
|
// there first.
|
|
if (!mRTL) {
|
|
leftCorrection = 0;
|
|
} else {
|
|
rightCorrection = 0;
|
|
}
|
|
}
|
|
// add left correction -- this fixes the problem of the italic 'f'
|
|
// e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo>
|
|
mX += leftCorrection;
|
|
mItalicCorrection = rightCorrection;
|
|
}
|
|
};
|
|
|
|
/* virtual */ nsresult
|
|
nsMathMLContainerFrame::Place(nsRenderingContext& aRenderingContext,
|
|
bool aPlaceOrigin,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
// This is needed in case this frame is empty (i.e., no child frames)
|
|
mBoundingMetrics = nsBoundingMetrics();
|
|
|
|
RowChildFrameIterator child(this);
|
|
nscoord ascent = 0, descent = 0;
|
|
while (child.Frame()) {
|
|
if (descent < child.Descent())
|
|
descent = child.Descent();
|
|
if (ascent < child.Ascent())
|
|
ascent = child.Ascent();
|
|
// add the child size
|
|
mBoundingMetrics.width = child.X();
|
|
mBoundingMetrics += child.BoundingMetrics();
|
|
++child;
|
|
}
|
|
// Add the italic correction at the end (including the last child).
|
|
// This gives a nice gap between math and non-math frames, and still
|
|
// gives the same math inter-spacing in case this frame connects to
|
|
// another math frame
|
|
mBoundingMetrics.width = child.X();
|
|
|
|
aDesiredSize.Width() = std::max(0, mBoundingMetrics.width);
|
|
aDesiredSize.Height() = ascent + descent;
|
|
aDesiredSize.SetBlockStartAscent(ascent);
|
|
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
|
|
|
|
mReference.x = 0;
|
|
mReference.y = aDesiredSize.BlockStartAscent();
|
|
|
|
//////////////////
|
|
// Place Children
|
|
|
|
if (aPlaceOrigin) {
|
|
PositionRowChildFrames(0, aDesiredSize.BlockStartAscent());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX,
|
|
nscoord aBaseline)
|
|
{
|
|
RowChildFrameIterator child(this);
|
|
while (child.Frame()) {
|
|
nscoord dx = aOffsetX + child.X();
|
|
nscoord dy = aBaseline - child.Ascent();
|
|
FinishReflowChild(child.Frame(), PresContext(), child.ReflowMetrics(),
|
|
nullptr, dx, dy, 0);
|
|
++child;
|
|
}
|
|
}
|
|
|
|
class ForceReflow : public nsIReflowCallback {
|
|
public:
|
|
virtual bool ReflowFinished() override {
|
|
return true;
|
|
}
|
|
virtual void ReflowCallbackCanceled() override {}
|
|
};
|
|
|
|
// We only need one of these so we just make it a static global, no need
|
|
// to dynamically allocate/destroy it.
|
|
static ForceReflow gForceReflow;
|
|
|
|
void
|
|
nsMathMLContainerFrame::SetIncrementScriptLevel(int32_t aChildIndex, bool aIncrement)
|
|
{
|
|
nsIFrame* child = PrincipalChildList().FrameAt(aChildIndex);
|
|
if (!child)
|
|
return;
|
|
nsIContent* content = child->GetContent();
|
|
if (!content->IsMathMLElement())
|
|
return;
|
|
nsMathMLElement* element = static_cast<nsMathMLElement*>(content);
|
|
|
|
if (element->GetIncrementScriptLevel() == aIncrement)
|
|
return;
|
|
|
|
// XXXroc this does a ContentStatesChanged, is it safe to call here? If
|
|
// not we should do it in a post-reflow callback.
|
|
element->SetIncrementScriptLevel(aIncrement, true);
|
|
PresContext()->PresShell()->PostReflowCallback(&gForceReflow);
|
|
}
|
|
|
|
// helpers to fix the inter-spacing when <math> is the only parent
|
|
// e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
|
|
|
|
static nscoord
|
|
GetInterFrameSpacingFor(int32_t aScriptLevel,
|
|
nsIFrame* aParentFrame,
|
|
nsIFrame* aChildFrame)
|
|
{
|
|
nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
|
|
if (!childFrame || aChildFrame == childFrame)
|
|
return 0;
|
|
|
|
int32_t carrySpace = 0;
|
|
eMathMLFrameType fromFrameType = eMathMLFrameType_UNKNOWN;
|
|
eMathMLFrameType prevFrameType = eMathMLFrameType_UNKNOWN;
|
|
eMathMLFrameType childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
|
|
childFrame = childFrame->GetNextSibling();
|
|
while (childFrame) {
|
|
prevFrameType = childFrameType;
|
|
childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
|
|
nscoord space = GetInterFrameSpacing(aScriptLevel,
|
|
prevFrameType, childFrameType, &fromFrameType, &carrySpace);
|
|
if (aChildFrame == childFrame) {
|
|
// get thinspace
|
|
nsStyleContext* parentContext = aParentFrame->StyleContext();
|
|
nscoord thinSpace = GetThinSpace(parentContext->StyleFont());
|
|
// we are done
|
|
return space * thinSpace;
|
|
}
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
|
|
NS_NOTREACHED("child not in the childlist of its parent");
|
|
return 0;
|
|
}
|
|
|
|
static nscoord
|
|
AddInterFrameSpacingToSize(nsHTMLReflowMetrics& aDesiredSize,
|
|
nsMathMLContainerFrame* aFrame)
|
|
{
|
|
nscoord gap = 0;
|
|
nsIFrame* parent = aFrame->GetParent();
|
|
nsIContent* parentContent = parent->GetContent();
|
|
if (MOZ_UNLIKELY(!parentContent)) {
|
|
return 0;
|
|
}
|
|
if (parentContent->IsAnyOfMathMLElements(nsGkAtoms::math,
|
|
nsGkAtoms::mtd_)) {
|
|
gap = GetInterFrameSpacingFor(aFrame->StyleFont()->mScriptLevel,
|
|
parent, aFrame);
|
|
// add our own italic correction
|
|
nscoord leftCorrection = 0, italicCorrection = 0;
|
|
aFrame->GetItalicCorrection(aDesiredSize.mBoundingMetrics,
|
|
leftCorrection, italicCorrection);
|
|
gap += leftCorrection;
|
|
if (gap) {
|
|
aDesiredSize.mBoundingMetrics.leftBearing += gap;
|
|
aDesiredSize.mBoundingMetrics.rightBearing += gap;
|
|
aDesiredSize.mBoundingMetrics.width += gap;
|
|
aDesiredSize.Width() += gap;
|
|
}
|
|
aDesiredSize.mBoundingMetrics.width += italicCorrection;
|
|
aDesiredSize.Width() += italicCorrection;
|
|
}
|
|
return gap;
|
|
}
|
|
|
|
nscoord
|
|
nsMathMLContainerFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
nscoord gap = 0;
|
|
gap = AddInterFrameSpacingToSize(aDesiredSize, this);
|
|
if (gap) {
|
|
// Shift our children to account for the correction
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
childFrame->SetPosition(childFrame->GetPosition() + nsPoint(gap, 0));
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
}
|
|
return gap;
|
|
}
|
|
|
|
/* static */ void
|
|
nsMathMLContainerFrame::DidReflowChildren(nsIFrame* aFirst, nsIFrame* aStop)
|
|
|
|
{
|
|
if (MOZ_UNLIKELY(!aFirst))
|
|
return;
|
|
|
|
for (nsIFrame* frame = aFirst;
|
|
frame != aStop;
|
|
frame = frame->GetNextSibling()) {
|
|
NS_ASSERTION(frame, "aStop isn't a sibling");
|
|
if (frame->GetStateBits() & NS_FRAME_IN_REFLOW) {
|
|
// finish off principal descendants, too
|
|
nsIFrame* grandchild = frame->GetFirstPrincipalChild();
|
|
if (grandchild)
|
|
DidReflowChildren(grandchild, nullptr);
|
|
|
|
frame->DidReflow(frame->PresContext(), nullptr,
|
|
nsDidReflowStatus::FINISHED);
|
|
}
|
|
}
|
|
}
|
|
|
|
// helper used by mstyle, mphantom, mpadded and mrow in their implementations
|
|
// of TransmitAutomaticData().
|
|
nsresult
|
|
nsMathMLContainerFrame::TransmitAutomaticDataForMrowLikeElement()
|
|
{
|
|
//
|
|
// One loop to check both conditions below:
|
|
//
|
|
// 1) whether all the children of the mrow-like element are space-like.
|
|
//
|
|
// The REC defines the following elements to be "space-like":
|
|
// * an mstyle, mphantom, or mpadded element, all of whose direct
|
|
// sub-expressions are space-like;
|
|
// * an mrow all of whose direct sub-expressions are space-like.
|
|
//
|
|
// 2) whether all but one child of the mrow-like element are space-like and
|
|
// this non-space-like child is an embellished operator.
|
|
//
|
|
// The REC defines the following elements to be embellished operators:
|
|
// * one of the elements mstyle, mphantom, or mpadded, such that an mrow
|
|
// containing the same arguments would be an embellished operator;
|
|
// * an mrow whose arguments consist (in any order) of one embellished
|
|
// operator and zero or more space-like elements.
|
|
//
|
|
nsIFrame *childFrame, *baseFrame;
|
|
bool embellishedOpFound = false;
|
|
nsEmbellishData embellishData;
|
|
|
|
for (childFrame = GetFirstPrincipalChild();
|
|
childFrame;
|
|
childFrame = childFrame->GetNextSibling()) {
|
|
nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
|
|
if (!mathMLFrame) break;
|
|
if (!mathMLFrame->IsSpaceLike()) {
|
|
if (embellishedOpFound) break;
|
|
baseFrame = childFrame;
|
|
GetEmbellishDataFrom(baseFrame, embellishData);
|
|
if (!NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)) break;
|
|
embellishedOpFound = true;
|
|
}
|
|
}
|
|
|
|
if (!childFrame) {
|
|
// we successfully went to the end of the loop. This means that one of
|
|
// condition 1) or 2) holds.
|
|
if (!embellishedOpFound) {
|
|
// the mrow-like element is space-like.
|
|
mPresentationData.flags |= NS_MATHML_SPACE_LIKE;
|
|
} else {
|
|
// the mrow-like element is an embellished operator.
|
|
// let the state of the embellished operator found bubble to us.
|
|
mPresentationData.baseFrame = baseFrame;
|
|
mEmbellishData = embellishData;
|
|
}
|
|
}
|
|
|
|
if (childFrame || !embellishedOpFound) {
|
|
// The element is not embellished operator
|
|
mPresentationData.baseFrame = nullptr;
|
|
mEmbellishData.flags = 0;
|
|
mEmbellishData.coreFrame = nullptr;
|
|
mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
|
|
mEmbellishData.leadingSpace = 0;
|
|
mEmbellishData.trailingSpace = 0;
|
|
}
|
|
|
|
if (childFrame || embellishedOpFound) {
|
|
// The element is not space-like
|
|
mPresentationData.flags &= ~NS_MATHML_SPACE_LIKE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/*static*/ void
|
|
nsMathMLContainerFrame::PropagateFrameFlagFor(nsIFrame* aFrame,
|
|
nsFrameState aFlags)
|
|
{
|
|
if (!aFrame || !aFlags)
|
|
return;
|
|
|
|
aFrame->AddStateBits(aFlags);
|
|
nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
|
|
while (childFrame) {
|
|
PropagateFrameFlagFor(childFrame, aFlags);
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsMathMLContainerFrame::ReportErrorToConsole(const char* errorMsgId,
|
|
const char16_t** aParams,
|
|
uint32_t aParamCount)
|
|
{
|
|
return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
|
|
NS_LITERAL_CSTRING("Layout: MathML"), mContent->OwnerDoc(),
|
|
nsContentUtils::eMATHML_PROPERTIES,
|
|
errorMsgId, aParams, aParamCount);
|
|
}
|
|
|
|
nsresult
|
|
nsMathMLContainerFrame::ReportParseError(const char16_t* aAttribute,
|
|
const char16_t* aValue)
|
|
{
|
|
const char16_t* argv[] =
|
|
{ aValue, aAttribute, mContent->NodeInfo()->NameAtom()->GetUTF16String() };
|
|
return ReportErrorToConsole("AttributeParsingError", argv, 3);
|
|
}
|
|
|
|
nsresult
|
|
nsMathMLContainerFrame::ReportChildCountError()
|
|
{
|
|
const char16_t* arg = mContent->NodeInfo()->NameAtom()->GetUTF16String();
|
|
return ReportErrorToConsole("ChildCountIncorrect", &arg, 1);
|
|
}
|
|
|
|
nsresult
|
|
nsMathMLContainerFrame::ReportInvalidChildError(nsIAtom* aChildTag)
|
|
{
|
|
const char16_t* argv[] =
|
|
{ aChildTag->GetUTF16String(),
|
|
mContent->NodeInfo()->NameAtom()->GetUTF16String() };
|
|
return ReportErrorToConsole("InvalidChild", argv, 2);
|
|
}
|
|
|
|
//==========================
|
|
|
|
nsContainerFrame*
|
|
NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
|
|
nsFrameState aFlags)
|
|
{
|
|
nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext);
|
|
it->SetFlags(aFlags);
|
|
return it;
|
|
}
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame)
|
|
|
|
NS_QUERYFRAME_HEAD(nsMathMLmathBlockFrame)
|
|
NS_QUERYFRAME_ENTRY(nsMathMLmathBlockFrame)
|
|
NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
|
|
|
|
nsContainerFrame*
|
|
NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
|
{
|
|
return new (aPresShell) nsMathMLmathInlineFrame(aContext);
|
|
}
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame)
|
|
|
|
NS_QUERYFRAME_HEAD(nsMathMLmathInlineFrame)
|
|
NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
|
|
NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame)
|