Files
palemoon27/dom/svg/SVGSVGElement.cpp
T
roytam1 049b3235ac import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1207494 - Part 8: Remove use of expression closure from dom/identity/. r=ferjm (34fac889f8)
- minor crash thing (13fe2827c2)
- Bug 1243359 - initialize mScrollSeriesNumber in all ScrollWheelInput constructors. r=roc (94163d8eca)
- fix misspatch (b380a7bbe4)
- Bug 1207412 - Make SystemTimeConverter use TimeStamp::Now() when converting times; r=karlt (f408549605)
- Bug 1212102 - Drop unnecessary assertion comparing timestamps in SystemTimeConverter; r=karlt (1efaa04b3f)
- Bug 1241302 - Slightly modernize this code. r=jst (c1e97bc069)
- bug 1216916 clean up when InvokeDragSessionWithImage/Selection() fails r=roc (9cdf71b717)
- Bug 1231329 - run ConvertToUnscaledDevPixels if *aPresContext is valid. r=roc (d10f23633b)
- Bug 1245927: Innerize before creating DOM files from the file picker. r=baku (8754128362)
- Bug 1155214 - Mark nsIBidiKeyboard as builtinclass; r=mrbkap (2d00326d2b)
- reorder (c3c1ba92c9)
- Bug 1236566 - add override declarations for widget/windows/GfxInfo.h; r=roc (940b40ed91)
- Bug 1242659: Part 1. More information on the mismatches. r=jrmuizel (78331ddad2)
- Block some Radeon drivers that crash on D3D9. (bug 1213107, r=jrmuizel) (482b66d313)
- Bug 1203199 - Blacklist DXVA on some older intel drivers for causing crashes. r=jrmuizel (f1827d4498)
- Bug 1203199 - Fix driver range with blacklist to avoid blacklisting other OSes. (2c2d547296)
- Bug 1203199 - Bustage fix. CLOSED TREE (3233d407f3)
- Bug 1178385. Enable DXVA on the G45 r=ajones (3c59a58a15)
- Bug 1188442 IMMHandler::GetSelection() should use static Selection instance when IME doesn't have focus and return its or mSelection's reference r=emk (5cc4b30433)
- Bug 1238899 Create hidden native caret for ATOK at composing in windowless plugin r=m_kato (40ef5bb4aa)
- Bug 1257919 part 1. Kill off nsIXSLTException. r=khuey (11f37643f2)
- Bug 1242072 - Change implementation of BaseProxyHandler::get() to follow ES6 [[Get]] specification. r=jorendorff (094a67f284)
- Bug 489636 - Add a GC u-bench test for property tree splitting via deletion; r=sfink (9282fec25f)
- Bug 1257919 part 2. Make nsIException and nsIStackFrame builtinclass, so we can start using [implicit_jscontext] on them. r=khuey (0e65fb8554)
- Bug 1257919 part 3. Make the various toString methods on exceptions take an explicit JSContext. r=khuey (b342fb4930)
- Bug 1257919 part 4. Stop returning StackFrame instances from exceptions::CreateStack. C++ callers of GetCurrentJSStack or exceptions::CreateStack always check for null anyway, and none of them seem to want this non-JS thing. r=khuey (d34036ccf9)
- Bug 1257919 part 5. Get rid of the now-unused StackFrame class. r=khuey (aa13af0dfe)
- Bug 1141222 - Create ScriptError with stack from Cu.reportError. r=bholley (b83ad26aa0)
- Bug 1208641 - Extract stack from DOM/XPC exception. r=bholley (e382b5f48c)
- Bug 1254380 part 1. Find the relevant window when the compartment we've got is an addon sandbox compartment in AutoJSAPI exception/warning reporting code. r=bholley (4b1f6c64a9)
- Bug 1250630 - remove PBackgroundTest and ifdef ENABLE_TEST blocks. r=khuey (d7b36860a3)
- Bug 1250963 part 1. Change NotifyRunnable::Dispatch to not require a JSContext. r=khuey (48e03c2fc2)
- Bug 1250963 part 2. Remove the unused JSContext argument of WorkerPrivate::ModifyBusyCount. r=khuey (0f5ca42be1)
- Bug 1250291 part 1. Stop pretending to report exceptions in MainThreadStopSyncLoopRunnable::PostDispatch. r=khuey (6e1c81cc06)
- Bug 1250291 part 2. Stop pretending to report exceptions in MainThreadWorkerSyncRunnable::PostDispatch. r=khuey (f3d846ea31)
- Bug 1250291 part 3. Stop pretending to report exceptions in WorkerDebuggerRunnable::PostDispatch. r=khuey (10bc1710d1)
- Bug 1250975. Stop passing a JSContext argument to WorkerRunnable::PreDispatch and its overrides. r=khuey (71ab9046f0)
- Bug 1255298. Just pass through the JSContext when passing through the NotificationOptions in notification code. r=wchen (552ae8e33e)
- Bug 1250185 part 2. Remove some JSContext stuff that's no longer needed in Notification code. r=khuey (20b99ef28b)
- Bug 1251045 part 8. Remove the JSContext argument from some nsINotificationStorageCallback methods. r=khuey (170c4afabc)
- Bug 1227015 P7 Supress unused ErrorResult exception if the worker runnable failed to dispatch. r=catalinb (23a3cc6f45)
- Bug 1251045 part 3. Remove the JSContext argument of WorkerRunnable::PostDispatch and its overrides. r=khuey (6a666d1a4a)
- Bug 1251045 part 2. Remove the JSContext argument of ModifyBusyCountFromWorker. r=khuey (105bc4f59e)
- Bug 1252130. Remove unnecessary JSContext argument from NotificationWorkerRunnable::WorkerRunInternal. r=khuey (642727f6f6)
- Bug 1252127. Remove unnecessary JSContext argument from PromiseWorkerProxy::CleanUp. r=khuey (339e1157d1)
- Bug 1251045 part 1. Make it possible to dispatch a ModifyBusyCountRunnable without a JSContext. r=khuey (665c1f81b4)
- Bug 1251045 part 4. Remove the JSContext argument of WorkerRunnable::Dispatch. r=khuey (6c5cdab2b7)
- Bug 1250185 part 1. nsIStructuredCloneContainer.initFromBase64 doesn't need a JSContext argument. r=khuey (d63a219209)
- fix (773c0393aa)
- Bug 1251045 part 5. Remove the JSContext argument from WorkerPrivateParent::Freeze/Thaw. r=khuey (cd419bfd2c)
- Bug 1251045 part 6. Remove the JSContext argument from some worker debugger methods that no longer need it. r=khuey (abcc437cb9)
- Bug 1251045 part 7. Remove the JSContext argument from some worker methods that no longer need it. r=khuey (3ddcbca08c)
- Bug 1249652 part 1. Add a method on ErrorResult to steal an exception, if any, from a JSContext. r=khuey (9c07f5044b)
- Bug 1229071 - importScripts should throw the correct error result in case the loading of one of scripts fails, r=bz (cd37645d76)
- Bug 1229970 Abort script loading start requests if a load has been canceled. r=khuey (a44d9128ca)
- Bug 1233171 Cancel channel when ScriptLoader is aborted during service worker script load. r=khuey (3b6ceafec7)
- Bug 1249351 part 1. When doing importScripts of multiple scripts in a service worker, make sure to track the cache streams per-loadinfo, instead of trying to make them all wait on the same stream. r=bkelly (e1ccea9685)
- Bug 1157544 - Fix a typo in the test; r=baku (8c0bf8f504)
- Bug 1249351 part 2. Clean up test_importscript.html and add multiple-url importScript() case. r=bz (52b0a31032)
- Bug 1249652 part 2. ScriptExecutorRunnable::WorkerRun should immediately move JS exceptions to its ErrorResult instead of allowing them to linger on the JSContext. r=baku,khuey (f628d3c8ba)
- Bug 1249652 part 3. Simplify way we handle canceling when ScriptLoaderRunnable::RunInternal fails by canceling things with its actual failure code, so we don't have to guess which failed loads are actual failures and which are just canceled via this mechanism. r=baku,khuey (388543fbc3)
- Bug 1251369. Use an AutoJSAPI that reports its own exceptions around the main runloop in workers. r=khuey (28b33287cd)
- Bug 1251518. Fix js::ErrorReportToString to make a bit more sense, and change worker code to not use it anyway, so it matches the mainthread code. r=bholley,terrence (a48a40de90)
- Bug 1254380 part 2. Go ahead and log the stack from our exception in AutoJSAPI::ReportException even if we don't have a window. r=bholley (64532b6017)
- Bug 1212328 - Clean up some JSErrorReport-related code. r=Waldo,bz (d5aa611edc)
- Bug 1255192 part 1. Remove the JSContext argument of JS::ExceptionStackOrNull. r=bholley (39b631d5ad)
- bug 1252687 - make the ctor for CycleCollectorStats constexpr r=mccr8 (9e8168d8ac)
- Bug 1254380 part 3. Skip firing error events for mainthread out of memory exceptions via AutoJSAPI::ReportException. r=bholley (d5e4e7dc29)
- Bug 1254230 kinda-fix. Make sure to never send script errors with stacks attached to the console service if the associated windows have already had FreeInnerObjects called on them. r=bholley (8c379fe0e0)
- Bug 1255192 part 2. Clean up the JSContext usage around xpc::FindExceptionStackForConsoleReport now that it just needs a JSContext for rooting. r=bholley (cade862491)
- Bug 1255201. Improve the stack handling in nsXPCComponents_Utils::ReportError for the DOMException case to include the stack from the DOMException. r=bholley (fc6c065284)
- Bug 1257919 part 6. Make the filename getter on JSStackFrame take an explicit JSContext. r=khuey (cde115b789)
- Bug 1257919 part 7. Make the name getter on JSStackFrame take an explicit JSContext. r=khuey (d7466e9eab)
- Bug 1257919 part 8. Make the line/column number getters on JSStackFrame take an explicit JSContext. r=khuey (b8766b98b2)
- Bug 1257919 part 9. Make the asyncCause/asyncCaller getters on JSStackFrame take an explicit JSContext. r=khuey (b776ff9c6d)
- Bug 1257919 part 10. Make the caller and formattedStack getters on JSStackFrame take an explicit JSContext. r=khuey (dc4d3d9091)
- Bug 1252091. Add/RemoveFeature don't need a JSContext argument. r=khuey (5be6253a67)
- Bug 1252123. Remove some unnecessary JSContext arguments from worker ScriptLoader methods. r=khuey (1657a35268)
- Bug 1255181. Remove AutoJSAPI::InitWithLegacyErrorReporting. r=bholley (5ee7ac506e)
- Bug 1254847 part 1. Take ownership of error reporting on the AutoEntryScript in nsXPCWrappedJSClass::DelegatedQueryInterface. r=bholley (62d987b030)
- make style wrong again.... (fa4e6cfbc1)
- Bug 1254847 part 2. Change nsJSNPRuntime to always use AutoEntryScript and always take ownership of error reporting. r=bholley (d24cb80622)
- Bug 1251655 - Remove support for JavaScript-global-constructor-prototype-alias. r=bz. (2b354ea423)
- Bug 1251655 - Remove support for JavaScript-DOM-class and JavaScript-DOM-interface. r=bz. (dd6786ea57)
- Bug 1251275. Switch to using an AutoEntryScript in WorkerPrivate::RunExpiredTimeouts. r=khuey (f10d1b0dec)
- Bug 1072144 part 1. Just release-assert that Promise::Resolve does not fail in service worker code. It can only do that on OOM or overrecursion anyway, and overrecursion is not likely if we're coming right off a runnable. r=khuey (ba4c89a3bc)
- Bug 1072144 part 2. When UnregisterWorker tries to ScheduleWorker and that throws, just suppress the exception: there is no good place to report it anyway. r=khuey (ca563ec5cd)
- Bug 1072144 part 3. Hoist the exception reporting out of WorkerRunnable::PostRun into WorkerRunnable::Run and make it unconditional. r=khuey (9914acd4a6)
- Bug 1252221. When GetOrCreateGlobalScope fails while trying to run a ScriptExecutorRunnable::WorkerRun, just suppress the exception, because there is no way to report it without a compartment to work with. r=khuey (abcc9df148)
- Bug 1072144 part 4. Add a WorkerRunnable::PreRun so that we can move worker global creation to it and always have an AutoEntryScript by the time we're evaluating the main worker script. r=khuey (007b528868)
- Bug 1251276 part 1. Change WorkerPrivate::CancelAllTimeouts to no longer call RunExpiredTimeouts. r=khuey (6b937370a1)
- Bug 1251276 part 2. Remove the JSContext argument of WorkerPrivate::CancelAllTimeouts. r=khuey (336b788e03)
- Bug 1251380. Change things so that WorkerPrivate::NotifyInternal (hopefully) never throws. r=khuey (1d4863f83a)
- Bug 1254846. Add an AutoEntryScript constructor that takes a JSObject instead of an nsIGlobalObject, for convenience. r=bholley (693857f9b0)
- Bug 1255706 part 1. Remove JSContext argument from WorkerPrivate constructor. r=khuey (0486bdb01c)
- Bug 1255706 part 2. Remove JSContext argument from WorkerPrivate::Terminate. r=khuey (f0b62de092)
- Bug 1257568. Remove the JSContext argument of WorkerPrivate::Kill. r=khuey (ebe7d247b5)
- Bug 1252189. Remove the unnecessary JSContext argument from WorkerPrivate::FreezeInternal/ThawInternal. r=khuey (22b137b05e)
- Bug 1119490 - Expose the URL constructor to WorkerDebuggerGlobalScope;r=khuey (6f4fc13e1e)
- Bug 1241841 - Clear the worker's debugger event queue before destroying its context;r=khuey (a1e8dd4b2d)
- Bug 1249950 - Add Performance Markers for MessagePort - patch 1 - remove non-useful 'explicit', r=smaug (8a014b53e6)
- Bug 1249950 - Add Performance Markers for MessagePort - patch 2, r=smaug, r=vporof (560caf0ae1)
- bug 1250486 - make the ComponentsSH ctor constexpr r=bz (1c441dbb0f)
- Bug 1257032: Make files in dom/workers actually build without unification. r=baku (6ab4ae0b65)
- Bug 1241522 - handle OOM in nsExpatDriver::HandleCharacterData; r=hsivonen (b652220cdc)
- Bug 1219482: Replace PRLogModuleInfo with LazyLogModule in various files.r=benjamin (5fd5e8dbc8)
- Bug 1238545 - Remove nsISimpleUnicharStreamFactory; r=froydnj (f0018c5b16)
- Bug 1257335. Replace some AutoSafeJSContext uses with AutoJSAPI or AutoJSContext uses. r=bholley (7baf79deb7)
- Bug 1247635 - Unify PostMessageRunnable and DispatchEventRunnable in MessagePort.cpp, r=smaug (df2765c215)
- Bug 1250572 - Force a parent object in MessagePort/Channel and in StructuredCloneHolder, r=smaug (2a929d59d1)
- Bug 1255375 - MessagePort should not leak if DispatchMessage() fails, r=smaug (0a5cdebfb0)
- Bug 1251272. Remove the dead code in ReportErrorRunnable::ReportError that could fail, and make it infallible. r=khuey (89e80694d0)
- Bug 1072144 part 5. Stop fiddling with compartments on the JSContext before calling PostRun in WorkerRunnable::Run. Add some documentation explaining what's going on. r=khuey (91f4bb6a7c)
- Bug 1072144 part 6. Switch WorkerRunnable::Run to calling TakeOwnershipOfErrorReporting on its AutoJSAPI/AutoEntryScript and remove the remaining JS_ReportPendingException callers in worker code. r=khuey (405d9e0282)
- Bug 1179548 - Close the windows opened in private browsing worker tests; r=baku (acf3430b69)
- Bug 1179753 - Use pushPrefEnv more in worker tests; r=baku (4bfd949c72)
- Bug 1134224 - More test for test_bug1132395.html, r=ehsan (626eb100cd)
- Bug 1134224 - onerror for test_bug1132395.html, r=ehsan (147195f57e)
- Bug 1207635 - get rid of dom.workers.sharedWorkers.enabled pref, r=khuey (22fe2965c6)
- Bug 1252592. JS warnings should not trigger error events on shared workers. r=khuey (7f05c2c00d)
- Bug 1251308; r=luke (3cbec95738)
- Bug 1246838 - Handle const qualifiers and references better in Variant. r=waldo (30da6e3ebf)
- Bug 1254565 - Allow passing matchers as rvalues to Variant::match. r=froydnj (1dd799fb0a)
- Bug 1246841 - Allow construction of Variant values using type inference. r=waldo (6ba3e6704c)
- Bug 1250666 - Forward Variant's move constructor argument correctly to the underlying variant type. r=waldo (7353a53bf0)
- Bug 1252185. Remove the dead "target" variable from CloseEventRunnable::WorkerRun. r=khuey (be172da10b)
- Bug 1253059: Use fallible allocation in the worker error reporter. r=baku (963222bdd4)
- Bug 1253199 - MessagePort should handle the dispatching a message when the parent window is gone, r=smaug (d552e3cd9e)
- Bug 1252839 - Remove some if stmt after allocation with 'new' - patch 2, r=bz (3c7eb959bc)
- Bug 1252839 - Remove some if stmt after allocation with 'new' - patch 1, r=bz (85067dbfb6)
- Bug 1254855. Switch AutoEntryScripts in nsDirectoryViewer to take ownership of error reporting. r=bholley (8c10532323)
- Bug 1252565 part 1. Push the script environment preparer bits up from XPCJSRuntime to CycleCollectedJSRuntime, because we need them on workers to do ctypes on workers properly. r=bholley (aecf2595dd)
- Bug 1252565 part 2. Make dom::WarningOnlyErrorReporter handle workers. r=bholley (f415b8868b)
- Bug 1244222 - Check for function interfaces in DelegatedQueryInterface. r=bz (d213eec83c)
- Bug 1254393. Take ownership of error reporting on the AutoEntryScript in nsXPCWrappedJSClass::CallMethod. r=bholley (4765b501c0)
- Bug 1251769 - Remove remaining references to MOZILLA_XPCOMRT_API from mfbt. r=froydnj (236adc1d4c)
- Bug 1247835 (part 0) - Minor comment and style tweaks in BinarySearch.h. r=luke. (db04793721)
- Bug 1244074 - Part 1: Move SheetParsingMode to a separate file. r=dholbert (290adaadea)
- Bug 1244074 - Part 2: Add HandleRefPtr for refcounting StyleSheetHandles. r=dholbert r=waldo (8549b24392)
- Bug 1244068 - Part 1: Add enum to represent the style system backend type. r=dholbert (c2e53feaf2)
- Bug 1244068 - Part 2: Add skeleton ServoStyleSet and a StyleSetHandle smart pointer. r=dholbert (71a5c28629)
- Bug 1244068 - Part 3: Factor out nsStyleSet getting in RestyleManager/ElementRestyler. r=dholbert (9e9f4f2e80)
- Bug 1245406: In ReflowBlockFrame, use Maybe<> to destroy & reconstruct a stack-allocated object, instead of an explicit call to destructor & placement 'new'. r=dbaron (d36858b818)
- Bug 1244068 - Part 4: Use StyleSetHandle instead of concrete style set class in most places. r=dholbert (93f9bcf799)
- Bug 1244074 - Part 3: Add skeleton ServoStyleSheet and a StyleSheetHandle smart pointer. r=dholbert (5b4fbdce38)
- Bug 1195173 - Apply CSP to preloaded styles within layout/style/Loader.cpp (r=bz) (d1ba920fd3)
- Bug 1245681 - Fix addOverrideStyleSheet. r=bz (64099eac73)
- Bug 1244074 - Part 4: Use StyleSheetHandle instead of concrete style sheet class in most places. r=dholberti (867470ac83)
- Bug 1246013 (part 1) - Store a copy of the ETLDEntry directly in DomainEntry instead of a pointer to it. r=jduell. (587b3a9fb7)
- Bug 1246013 (part 2) - Change "explicit/xpcom/effective-TLD-service" path to the more sensible "explicit/network/effective-TLD-service". r=jduell. (2e0df6f28f)
- Bug 1247835 (part 1) - Use binary search instead of a hash table in nsEffectiveTLDService. r=jduell. (685dae01c0)
- Bug 1257121 part 1 - Use struct for passing some params of font metrics. r=jfkthame (1dfd7a7429)
- Bug 1257121 part 2 - Merge nsFontMetrics::Init to the constructor and remove unused failure handling code. r=jfkthame (0cdc441511)
- Bug 1257121 part 3 - Add GetInflatedFontMetricsForFrame function to simplify a common use pattern. r=jfkthame (d9630ebd1b)
- Bug 1257121 part 4 - Use return value rather than out param to retur font metrics. r=jfkthame (c9a5b1d1dd)
- Bug 1257121 part 5 - Make nsDeviceContext::mFontCache a RefPtr. r=jfkthame (9c53238a6a)
2024-01-29 15:26:23 +08:00

1193 lines
35 KiB
C++

/* -*- Mode: C++; tab-width: 8; 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/. */
#include <stdint.h>
#include "mozilla/ArrayUtils.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/Likely.h"
#include "nsGkAtoms.h"
#include "nsLayoutUtils.h"
#include "nsLayoutStylesheetCache.h"
#include "DOMSVGNumber.h"
#include "DOMSVGLength.h"
#include "nsSVGAngle.h"
#include "nsCOMPtr.h"
#include "nsIPresShell.h"
#include "nsContentUtils.h"
#include "nsIDocument.h"
#include "mozilla/dom/SVGMatrix.h"
#include "DOMSVGPoint.h"
#include "nsIFrame.h"
#include "nsFrameSelection.h"
#include "nsISVGSVGFrame.h" //XXX
#include "mozilla/dom/SVGRect.h"
#include "nsError.h"
#include "nsISVGChildFrame.h"
#include "mozilla/dom/SVGSVGElement.h"
#include "mozilla/dom/SVGSVGElementBinding.h"
#include "nsSVGUtils.h"
#include "mozilla/dom/SVGViewElement.h"
#include "nsStyleUtil.h"
#include "SVGContentUtils.h"
#include "nsSMILTimeContainer.h"
#include "nsSMILAnimationController.h"
#include "nsSMILTypes.h"
#include "SVGAngle.h"
#include <algorithm>
#include "prtime.h"
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT_CHECK_PARSER(SVG)
using namespace mozilla::gfx;
namespace mozilla {
namespace dom {
class SVGAnimatedLength;
JSObject*
SVGSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
{
return SVGSVGElementBinding::Wrap(aCx, this, aGivenProto);
}
NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMSVGTranslatePoint, nsISVGPoint,
mElement)
NS_IMPL_ADDREF_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
NS_IMPL_RELEASE_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGTranslatePoint)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
// We have to qualify nsISVGPoint because NS_GET_IID looks for a class in the
// global namespace
NS_INTERFACE_MAP_ENTRY(mozilla::nsISVGPoint)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
SVGSVGElement::~SVGSVGElement()
{
}
DOMSVGPoint*
DOMSVGTranslatePoint::Copy()
{
return new DOMSVGPoint(mPt.GetX(), mPt.GetY());
}
nsISupports*
DOMSVGTranslatePoint::GetParentObject()
{
return static_cast<nsIDOMSVGElement*>(mElement);
}
void
DOMSVGTranslatePoint::SetX(float aValue, ErrorResult& rv)
{
mElement->SetCurrentTranslate(aValue, mPt.GetY());
}
void
DOMSVGTranslatePoint::SetY(float aValue, ErrorResult& rv)
{
mElement->SetCurrentTranslate(mPt.GetX(), aValue);
}
already_AddRefed<nsISVGPoint>
DOMSVGTranslatePoint::MatrixTransform(SVGMatrix& matrix)
{
float a = matrix.A(), b = matrix.B(), c = matrix.C();
float d = matrix.D(), e = matrix.E(), f = matrix.F();
float x = mPt.GetX();
float y = mPt.GetY();
nsCOMPtr<nsISVGPoint> point = new DOMSVGPoint(a*x + c*y + e, b*x + d*y + f);
return point.forget();
}
SVGView::SVGView()
{
mZoomAndPan.Init(SVGSVGElement::ZOOMANDPAN,
SVG_ZOOMANDPAN_MAGNIFY);
mViewBox.Init();
mPreserveAspectRatio.Init();
}
nsSVGElement::LengthInfo SVGSVGElement::sLengthInfo[4] =
{
{ &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
{ &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
{ &nsGkAtoms::width, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X },
{ &nsGkAtoms::height, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y },
};
nsSVGEnumMapping SVGSVGElement::sZoomAndPanMap[] = {
{&nsGkAtoms::disable, SVG_ZOOMANDPAN_DISABLE},
{&nsGkAtoms::magnify, SVG_ZOOMANDPAN_MAGNIFY},
{nullptr, 0}
};
nsSVGElement::EnumInfo SVGSVGElement::sEnumInfo[1] =
{
{ &nsGkAtoms::zoomAndPan,
sZoomAndPanMap,
SVG_ZOOMANDPAN_MAGNIFY
}
};
//----------------------------------------------------------------------
// nsISupports methods
NS_IMPL_CYCLE_COLLECTION_CLASS(SVGSVGElement)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGSVGElement,
SVGSVGElementBase)
if (tmp->mTimedDocumentRoot) {
tmp->mTimedDocumentRoot->Unlink();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGSVGElement,
SVGSVGElementBase)
if (tmp->mTimedDocumentRoot) {
tmp->mTimedDocumentRoot->Traverse(&cb);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(SVGSVGElement,SVGSVGElementBase)
NS_IMPL_RELEASE_INHERITED(SVGSVGElement,SVGSVGElementBase)
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGSVGElement)
NS_INTERFACE_TABLE_INHERITED(SVGSVGElement, nsIDOMNode, nsIDOMElement,
nsIDOMSVGElement)
NS_INTERFACE_TABLE_TAIL_INHERITING(SVGSVGElementBase)
//----------------------------------------------------------------------
// Implementation
SVGSVGElement::SVGSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
FromParser aFromParser)
: SVGSVGElementBase(aNodeInfo),
mViewportWidth(0),
mViewportHeight(0),
mCurrentTranslate(0.0f, 0.0f),
mCurrentScale(1.0f),
mPreviousTranslate(0.0f, 0.0f),
mPreviousScale(1.0f),
mStartAnimationOnBindToTree(aFromParser == NOT_FROM_PARSER ||
aFromParser == FROM_PARSER_FRAGMENT ||
aFromParser == FROM_PARSER_XSLT),
mImageNeedsTransformInvalidation(false),
mIsPaintingSVGImageElement(false),
mHasChildrenOnlyTransform(false)
{
}
//----------------------------------------------------------------------
// nsIDOMNode methods
// From NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSVGElement)
nsresult
SVGSVGElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const
{
*aResult = nullptr;
already_AddRefed<mozilla::dom::NodeInfo> ni = RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();
SVGSVGElement *it = new SVGSVGElement(ni, NOT_FROM_PARSER);
nsCOMPtr<nsINode> kungFuDeathGrip = it;
nsresult rv1 = it->Init();
nsresult rv2 = const_cast<SVGSVGElement*>(this)->CopyInnerTo(it);
if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
kungFuDeathGrip.swap(*aResult);
}
return NS_FAILED(rv1) ? rv1 : rv2;
}
//----------------------------------------------------------------------
// nsIDOMSVGSVGElement methods:
already_AddRefed<SVGAnimatedLength>
SVGSVGElement::X()
{
return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
}
already_AddRefed<SVGAnimatedLength>
SVGSVGElement::Y()
{
return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
}
already_AddRefed<SVGAnimatedLength>
SVGSVGElement::Width()
{
return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
}
already_AddRefed<SVGAnimatedLength>
SVGSVGElement::Height()
{
return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
}
float
SVGSVGElement::PixelUnitToMillimeterX()
{
return MM_PER_INCH_FLOAT / 96;
}
float
SVGSVGElement::PixelUnitToMillimeterY()
{
return PixelUnitToMillimeterX();
}
float
SVGSVGElement::ScreenPixelToMillimeterX()
{
return MM_PER_INCH_FLOAT / 96;
}
float
SVGSVGElement::ScreenPixelToMillimeterY()
{
return ScreenPixelToMillimeterX();
}
bool
SVGSVGElement::UseCurrentView()
{
return mSVGView || mCurrentViewID;
}
float
SVGSVGElement::CurrentScale()
{
return mCurrentScale;
}
#define CURRENT_SCALE_MAX 16.0f
#define CURRENT_SCALE_MIN 0.0625f
void
SVGSVGElement::SetCurrentScale(float aCurrentScale)
{
SetCurrentScaleTranslate(aCurrentScale,
mCurrentTranslate.GetX(), mCurrentTranslate.GetY());
}
already_AddRefed<nsISVGPoint>
SVGSVGElement::CurrentTranslate()
{
nsCOMPtr<nsISVGPoint> point = new DOMSVGTranslatePoint(&mCurrentTranslate, this);
return point.forget();
}
uint32_t
SVGSVGElement::SuspendRedraw(uint32_t max_wait_milliseconds)
{
// suspendRedraw is a no-op in Mozilla, so it doesn't matter what
// we return
return 1;
}
void
SVGSVGElement::UnsuspendRedraw(uint32_t suspend_handle_id)
{
// no-op
}
void
SVGSVGElement::UnsuspendRedrawAll()
{
// no-op
}
void
SVGSVGElement::ForceRedraw()
{
// no-op
}
void
SVGSVGElement::PauseAnimations()
{
if (mTimedDocumentRoot) {
mTimedDocumentRoot->Pause(nsSMILTimeContainer::PAUSE_SCRIPT);
}
// else we're not the outermost <svg> or not bound to a tree, so silently fail
}
void
SVGSVGElement::UnpauseAnimations()
{
if (mTimedDocumentRoot) {
mTimedDocumentRoot->Resume(nsSMILTimeContainer::PAUSE_SCRIPT);
}
// else we're not the outermost <svg> or not bound to a tree, so silently fail
}
bool
SVGSVGElement::AnimationsPaused()
{
nsSMILTimeContainer* root = GetTimedDocumentRoot();
return root && root->IsPausedByType(nsSMILTimeContainer::PAUSE_SCRIPT);
}
float
SVGSVGElement::GetCurrentTime()
{
nsSMILTimeContainer* root = GetTimedDocumentRoot();
if (root) {
double fCurrentTimeMs = double(root->GetCurrentTime());
return (float)(fCurrentTimeMs / PR_MSEC_PER_SEC);
} else {
return 0.f;
}
}
void
SVGSVGElement::SetCurrentTime(float seconds)
{
if (mTimedDocumentRoot) {
// Make sure the timegraph is up-to-date
FlushAnimations();
double fMilliseconds = double(seconds) * PR_MSEC_PER_SEC;
// Round to nearest whole number before converting, to avoid precision
// errors
nsSMILTime lMilliseconds = int64_t(NS_round(fMilliseconds));
mTimedDocumentRoot->SetCurrentTime(lMilliseconds);
AnimationNeedsResample();
// Trigger synchronous sample now, to:
// - Make sure we get an up-to-date paint after this method
// - re-enable event firing (it got disabled during seeking, and it
// doesn't get re-enabled until the first sample after the seek -- so
// let's make that happen now.)
FlushAnimations();
}
// else we're not the outermost <svg> or not bound to a tree, so silently fail
}
void
SVGSVGElement::DeselectAll()
{
nsIFrame* frame = GetPrimaryFrame();
if (frame) {
RefPtr<nsFrameSelection> frameSelection = frame->GetFrameSelection();
frameSelection->ClearNormalSelection();
}
}
already_AddRefed<DOMSVGNumber>
SVGSVGElement::CreateSVGNumber()
{
RefPtr<DOMSVGNumber> number = new DOMSVGNumber(ToSupports(this));
return number.forget();
}
already_AddRefed<DOMSVGLength>
SVGSVGElement::CreateSVGLength()
{
nsCOMPtr<DOMSVGLength> length = new DOMSVGLength();
return length.forget();
}
already_AddRefed<SVGAngle>
SVGSVGElement::CreateSVGAngle()
{
nsSVGAngle* angle = new nsSVGAngle();
angle->Init();
RefPtr<SVGAngle> svgangle = new SVGAngle(angle, this, SVGAngle::CreatedValue);
return svgangle.forget();
}
already_AddRefed<nsISVGPoint>
SVGSVGElement::CreateSVGPoint()
{
nsCOMPtr<nsISVGPoint> point = new DOMSVGPoint(0, 0);
return point.forget();
}
already_AddRefed<SVGMatrix>
SVGSVGElement::CreateSVGMatrix()
{
RefPtr<SVGMatrix> matrix = new SVGMatrix();
return matrix.forget();
}
already_AddRefed<SVGIRect>
SVGSVGElement::CreateSVGRect()
{
return NS_NewSVGRect(this);
}
already_AddRefed<SVGTransform>
SVGSVGElement::CreateSVGTransform()
{
RefPtr<SVGTransform> transform = new SVGTransform();
return transform.forget();
}
already_AddRefed<SVGTransform>
SVGSVGElement::CreateSVGTransformFromMatrix(SVGMatrix& matrix)
{
RefPtr<SVGTransform> transform = new SVGTransform(matrix.GetMatrix());
return transform.forget();
}
//----------------------------------------------------------------------
already_AddRefed<SVGAnimatedRect>
SVGSVGElement::ViewBox()
{
return mViewBox.ToSVGAnimatedRect(this);
}
already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
SVGSVGElement::PreserveAspectRatio()
{
return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
}
uint16_t
SVGSVGElement::ZoomAndPan()
{
return mEnumAttributes[ZOOMANDPAN].GetAnimValue();
}
void
SVGSVGElement::SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv)
{
if (aZoomAndPan == SVG_ZOOMANDPAN_DISABLE ||
aZoomAndPan == SVG_ZOOMANDPAN_MAGNIFY) {
mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this);
return;
}
rv.ThrowRangeError<MSG_INVALID_ZOOMANDPAN_VALUE_ERROR>();
}
//----------------------------------------------------------------------
// helper methods for implementing SVGZoomEvent:
void
SVGSVGElement::SetCurrentScaleTranslate(float s, float x, float y)
{
if (s == mCurrentScale &&
x == mCurrentTranslate.GetX() && y == mCurrentTranslate.GetY()) {
return;
}
// Prevent bizarre behaviour and maxing out of CPU and memory by clamping
if (s < CURRENT_SCALE_MIN)
s = CURRENT_SCALE_MIN;
else if (s > CURRENT_SCALE_MAX)
s = CURRENT_SCALE_MAX;
// IMPORTANT: If either mCurrentTranslate *or* mCurrentScale is changed then
// mPreviousTranslate_x, mPreviousTranslate_y *and* mPreviousScale must all
// be updated otherwise SVGZoomEvents will end up with invalid data. I.e. an
// SVGZoomEvent's properties previousScale and previousTranslate must contain
// the state of currentScale and currentTranslate immediately before the
// change that caused the event's dispatch, which is *not* necessarily the
// same thing as the values of currentScale and currentTranslate prior to
// their own last change.
mPreviousScale = mCurrentScale;
mPreviousTranslate = mCurrentTranslate;
mCurrentScale = s;
mCurrentTranslate = SVGPoint(x, y);
// now dispatch the appropriate event if we are the root element
nsIDocument* doc = GetUncomposedDoc();
if (doc) {
nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
if (presShell && IsRoot()) {
nsEventStatus status = nsEventStatus_eIgnore;
if (mPreviousScale != mCurrentScale) {
InternalSVGZoomEvent svgZoomEvent(true, eSVGZoom);
presShell->HandleDOMEventWithTarget(this, &svgZoomEvent, &status);
} else {
WidgetEvent svgScrollEvent(true, eSVGScroll);
presShell->HandleDOMEventWithTarget(this, &svgScrollEvent, &status);
}
InvalidateTransformNotifyFrame();
}
}
}
void
SVGSVGElement::SetCurrentTranslate(float x, float y)
{
SetCurrentScaleTranslate(mCurrentScale, x, y);
}
nsSMILTimeContainer*
SVGSVGElement::GetTimedDocumentRoot()
{
if (mTimedDocumentRoot) {
return mTimedDocumentRoot;
}
// We must not be the outermost <svg> element, try to find it
SVGSVGElement *outerSVGElement =
SVGContentUtils::GetOuterSVGElement(this);
if (outerSVGElement) {
return outerSVGElement->GetTimedDocumentRoot();
}
// invalid structure
return nullptr;
}
//----------------------------------------------------------------------
// nsIContent methods
NS_IMETHODIMP_(bool)
SVGSVGElement::IsAttributeMapped(const nsIAtom* name) const
{
// We want to map the 'width' and 'height' attributes into style for
// outer-<svg>, except when the attributes aren't set (since their default
// values of '100%' can cause unexpected and undesirable behaviour for SVG
// inline in HTML). We rely on nsSVGElement::UpdateContentStyleRule() to
// prevent mapping of the default values into style (it only maps attributes
// that are set). We also rely on a check in nsSVGElement::
// UpdateContentStyleRule() to prevent us mapping the attributes when they're
// given a <length> value that is not currently recognized by the SVG
// specification.
if (!IsInner() && (name == nsGkAtoms::width || name == nsGkAtoms::height)) {
return true;
}
static const MappedAttributeEntry* const map[] = {
sColorMap,
sFEFloodMap,
sFillStrokeMap,
sFiltersMap,
sFontSpecificationMap,
sGradientStopMap,
sGraphicsMap,
sLightingEffectsMap,
sMarkersMap,
sTextContentElementsMap,
sViewportsMap
};
return FindAttributeDependence(name, map) ||
SVGSVGElementBase::IsAttributeMapped(name);
}
//----------------------------------------------------------------------
// nsIContent methods:
nsresult
SVGSVGElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
{
if (aVisitor.mEvent->mMessage == eSVGLoad) {
if (mTimedDocumentRoot) {
mTimedDocumentRoot->Begin();
// Set 'resample needed' flag, so that if any script calls a DOM method
// that requires up-to-date animations before our first sample callback,
// we'll force a synchronous sample.
AnimationNeedsResample();
}
}
return SVGSVGElementBase::PreHandleEvent(aVisitor);
}
bool
SVGSVGElement::IsEventAttributeName(nsIAtom* aName)
{
/* The events in EventNameType_SVGSVG are for events that are only
applicable to outermost 'svg' elements. We don't check if we're an outer
'svg' element in case we're not inserted into the document yet, but since
the target of the events in question will always be the outermost 'svg'
element, this shouldn't cause any real problems.
*/
return nsContentUtils::IsEventAttributeName(aName,
(EventNameType_SVGGraphic | EventNameType_SVGSVG));
}
//----------------------------------------------------------------------
// nsSVGElement overrides
// Helper for GetViewBoxTransform on root <svg> node
// * aLength: internal value for our <svg> width or height attribute.
// * aViewportLength: length of the corresponding dimension of the viewport.
// * aSelf: the outermost <svg> node itself.
// NOTE: aSelf is not an ancestor viewport element, so it can't be used to
// resolve percentage lengths. (It can only be used to resolve
// 'em'/'ex'-valued units).
inline float
ComputeSynthesizedViewBoxDimension(const nsSVGLength2& aLength,
float aViewportLength,
const SVGSVGElement* aSelf)
{
if (aLength.IsPercentage()) {
return aViewportLength * aLength.GetAnimValInSpecifiedUnits() / 100.0f;
}
return aLength.GetAnimValue(const_cast<SVGSVGElement*>(aSelf));
}
//----------------------------------------------------------------------
// public helpers:
gfx::Matrix
SVGSVGElement::GetViewBoxTransform() const
{
float viewportWidth, viewportHeight;
if (IsInner()) {
SVGSVGElement *ctx = GetCtx();
viewportWidth = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
viewportHeight = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
} else {
viewportWidth = mViewportWidth;
viewportHeight = mViewportHeight;
}
if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
}
nsSVGViewBoxRect viewBox =
GetViewBoxWithSynthesis(viewportWidth, viewportHeight);
if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
}
return SVGContentUtils::GetViewBoxTransform(viewportWidth, viewportHeight,
viewBox.x, viewBox.y,
viewBox.width, viewBox.height,
GetPreserveAspectRatioWithOverride());
}
void
SVGSVGElement::UpdateHasChildrenOnlyTransform()
{
bool hasChildrenOnlyTransform =
HasViewBoxOrSyntheticViewBox() ||
(IsRoot() && (mCurrentTranslate != SVGPoint(0.0f, 0.0f) ||
mCurrentScale != 1.0f));
mHasChildrenOnlyTransform = hasChildrenOnlyTransform;
}
void
SVGSVGElement::ChildrenOnlyTransformChanged(uint32_t aFlags)
{
// Avoid wasteful calls:
MOZ_ASSERT(!(GetPrimaryFrame()->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
"Non-display SVG frames don't maintain overflow rects");
nsChangeHint changeHint;
bool hadChildrenOnlyTransform = mHasChildrenOnlyTransform;
UpdateHasChildrenOnlyTransform();
if (hadChildrenOnlyTransform != mHasChildrenOnlyTransform) {
// Reconstruct the frame tree to handle stacking context changes:
// XXXjwatt don't do this for root-<svg> or even outer-<svg>?
changeHint = nsChangeHint_ReconstructFrame;
} else {
// We just assume the old and new transforms are different.
changeHint = nsChangeHint(nsChangeHint_UpdateOverflow |
nsChangeHint_ChildrenOnlyTransform);
}
// If we're not reconstructing the frame tree, then we only call
// PostRestyleEvent if we're not being called under reflow to avoid recursing
// to death. See bug 767056 comments 10 and 12. Since our nsSVGOuterSVGFrame
// is being reflowed we're going to invalidate and repaint its entire area
// anyway (which will include our children).
if ((changeHint & nsChangeHint_ReconstructFrame) ||
!(aFlags & eDuringReflow)) {
nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
}
}
nsresult
SVGSVGElement::BindToTree(nsIDocument* aDocument,
nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
nsSMILAnimationController* smilController = nullptr;
if (aDocument) {
smilController = aDocument->GetAnimationController();
if (smilController) {
// SMIL is enabled in this document
if (WillBeOutermostSVG(aParent, aBindingParent)) {
// We'll be the outermost <svg> element. We'll need a time container.
if (!mTimedDocumentRoot) {
mTimedDocumentRoot = new nsSMILTimeContainer();
}
} else {
// We're a child of some other <svg> element, so we don't need our own
// time container. However, we need to make sure that we'll get a
// kick-start if we get promoted to be outermost later on.
mTimedDocumentRoot = nullptr;
mStartAnimationOnBindToTree = true;
}
}
}
nsresult rv = SVGSVGElementBase::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv,rv);
nsIDocument* doc = GetComposedDoc();
if (doc) {
// Setup the style sheet during binding, not element construction,
// because we could move the root SVG element from the document
// that created it to another document.
auto cache = nsLayoutStylesheetCache::For(doc->GetStyleBackendType());
doc->EnsureOnDemandBuiltInUASheet(cache->SVGSheet());
}
if (mTimedDocumentRoot && smilController) {
rv = mTimedDocumentRoot->SetParent(smilController);
if (mStartAnimationOnBindToTree) {
mTimedDocumentRoot->Begin();
mStartAnimationOnBindToTree = false;
}
}
return rv;
}
void
SVGSVGElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
if (mTimedDocumentRoot) {
mTimedDocumentRoot->SetParent(nullptr);
}
SVGSVGElementBase::UnbindFromTree(aDeep, aNullParent);
}
//----------------------------------------------------------------------
// implementation helpers
bool
SVGSVGElement::WillBeOutermostSVG(nsIContent* aParent,
nsIContent* aBindingParent) const
{
nsIContent* parent = aBindingParent ? aBindingParent : aParent;
while (parent && parent->IsSVGElement()) {
if (parent->IsSVGElement(nsGkAtoms::foreignObject)) {
// SVG in a foreignObject must have its own <svg> (nsSVGOuterSVGFrame).
return false;
}
if (parent->IsSVGElement(nsGkAtoms::svg)) {
return false;
}
parent = parent->GetParent();
}
return true;
}
void
SVGSVGElement::InvalidateTransformNotifyFrame()
{
nsISVGSVGFrame* svgframe = do_QueryFrame(GetPrimaryFrame());
// might fail this check if we've failed conditional processing
if (svgframe) {
svgframe->NotifyViewportOrTransformChanged(
nsISVGChildFrame::TRANSFORM_CHANGED);
}
}
bool
SVGSVGElement::HasPreserveAspectRatio()
{
return HasAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio) ||
mPreserveAspectRatio.IsAnimated();
}
SVGViewElement*
SVGSVGElement::GetCurrentViewElement() const
{
if (mCurrentViewID) {
//XXXsmaug It is unclear how this should work in case we're in Shadow DOM.
nsIDocument* doc = GetUncomposedDoc();
if (doc) {
Element *element = doc->GetElementById(*mCurrentViewID);
if (element && element->IsSVGElement(nsGkAtoms::view)) {
return static_cast<SVGViewElement*>(element);
}
}
}
return nullptr;
}
nsSVGViewBoxRect
SVGSVGElement::GetViewBoxWithSynthesis(
float aViewportWidth, float aViewportHeight) const
{
// The logic here should match HasViewBoxRect().
SVGViewElement* viewElement = GetCurrentViewElement();
if (viewElement && viewElement->mViewBox.HasRect()) {
return viewElement->mViewBox.GetAnimValue();
}
if (mSVGView && mSVGView->mViewBox.HasRect()) {
return mSVGView->mViewBox.GetAnimValue();
}
if (mViewBox.HasRect()) {
return mViewBox.GetAnimValue();
}
if (ShouldSynthesizeViewBox()) {
// Special case -- fake a viewBox, using height & width attrs.
// (Use |this| as context, since if we get here, we're outermost <svg>.)
return nsSVGViewBoxRect(0, 0,
ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
mViewportWidth, this),
ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
mViewportHeight, this));
}
// No viewBox attribute, so we shouldn't auto-scale. This is equivalent
// to having a viewBox that exactly matches our viewport size.
return nsSVGViewBoxRect(0, 0, aViewportWidth, aViewportHeight);
}
SVGPreserveAspectRatio
SVGSVGElement::GetPreserveAspectRatioWithOverride() const
{
nsIDocument* doc = GetUncomposedDoc();
if (doc && doc->IsBeingUsedAsImage()) {
const SVGPreserveAspectRatio *pAROverridePtr = GetPreserveAspectRatioProperty();
if (pAROverridePtr) {
return *pAROverridePtr;
}
}
SVGViewElement* viewElement = GetCurrentViewElement();
// This check is equivalent to "!HasViewBoxRect() && ShouldSynthesizeViewBox()".
// We're just holding onto the viewElement that HasViewBoxRect() would look up,
// so that we don't have to look it up again later.
if (!((viewElement && viewElement->mViewBox.HasRect()) ||
(mSVGView && mSVGView->mViewBox.HasRect()) ||
mViewBox.HasRect()) &&
ShouldSynthesizeViewBox()) {
// If we're synthesizing a viewBox, use preserveAspectRatio="none";
return SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE, SVG_MEETORSLICE_SLICE);
}
if (viewElement && viewElement->mPreserveAspectRatio.IsExplicitlySet()) {
return viewElement->mPreserveAspectRatio.GetAnimValue();
}
if (mSVGView && mSVGView->mPreserveAspectRatio.IsExplicitlySet()) {
return mSVGView->mPreserveAspectRatio.GetAnimValue();
}
return mPreserveAspectRatio.GetAnimValue();
}
//----------------------------------------------------------------------
// SVGSVGElement
float
SVGSVGElement::GetLength(uint8_t aCtxType)
{
float h, w;
SVGViewElement* viewElement = GetCurrentViewElement();
const nsSVGViewBoxRect* viewbox = nullptr;
// The logic here should match HasViewBoxRect().
if (viewElement && viewElement->mViewBox.HasRect()) {
viewbox = &viewElement->mViewBox.GetAnimValue();
} else if (mSVGView && mSVGView->mViewBox.HasRect()) {
viewbox = &mSVGView->mViewBox.GetAnimValue();
} else if (mViewBox.HasRect()) {
viewbox = &mViewBox.GetAnimValue();
}
if (viewbox) {
w = viewbox->width;
h = viewbox->height;
} else if (IsInner()) {
SVGSVGElement *ctx = GetCtx();
w = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
h = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
} else if (ShouldSynthesizeViewBox()) {
w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
mViewportWidth, this);
h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
mViewportHeight, this);
} else {
w = mViewportWidth;
h = mViewportHeight;
}
w = std::max(w, 0.0f);
h = std::max(h, 0.0f);
switch (aCtxType) {
case SVGContentUtils::X:
return w;
case SVGContentUtils::Y:
return h;
case SVGContentUtils::XY:
return float(SVGContentUtils::ComputeNormalizedHypotenuse(w, h));
}
return 0;
}
//----------------------------------------------------------------------
// nsSVGElement methods
/* virtual */ gfxMatrix
SVGSVGElement::PrependLocalTransformsTo(
const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const
{
// 'transform' attribute (or an override from a fragment identifier):
gfxMatrix fromUserSpace =
SVGContentUtils::PrependLocalTransformsTo(
aMatrix, aWhich, mAnimateMotionTransform,
mSVGView && mSVGView->mTransforms ? mSVGView->mTransforms : mTransforms);
if (aWhich == eUserSpaceToParent) {
return fromUserSpace;
}
if (IsInner()) {
float x, y;
const_cast<SVGSVGElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
if (aWhich == eAllTransforms) {
// the common case
return ThebesMatrix(GetViewBoxTransform()) * gfxMatrix::Translation(x, y) * fromUserSpace;
}
MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
return ThebesMatrix(GetViewBoxTransform()) * gfxMatrix::Translation(x, y) * aMatrix;
}
if (IsRoot()) {
gfxMatrix zoomPanTM;
zoomPanTM.Translate(gfxPoint(mCurrentTranslate.GetX(), mCurrentTranslate.GetY()));
zoomPanTM.Scale(mCurrentScale, mCurrentScale);
return ThebesMatrix(GetViewBoxTransform()) * zoomPanTM * fromUserSpace;
}
// outer-<svg>, but inline in some other content:
return ThebesMatrix(GetViewBoxTransform()) * fromUserSpace;
}
nsSVGAnimatedTransformList*
SVGSVGElement::GetAnimatedTransformList(uint32_t aFlags)
{
if (!(aFlags & DO_ALLOCATE) && mSVGView && mSVGView->mTransforms) {
return mSVGView->mTransforms;
}
return SVGSVGElementBase::GetAnimatedTransformList(aFlags);
}
/* virtual */ bool
SVGSVGElement::HasValidDimensions() const
{
return !IsInner() ||
((!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
(!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0));
}
nsSVGElement::LengthAttributesInfo
SVGSVGElement::GetLengthInfo()
{
return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
ArrayLength(sLengthInfo));
}
nsSVGElement::EnumAttributesInfo
SVGSVGElement::GetEnumInfo()
{
return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
ArrayLength(sEnumInfo));
}
nsSVGViewBox *
SVGSVGElement::GetViewBox()
{
return &mViewBox;
}
SVGAnimatedPreserveAspectRatio *
SVGSVGElement::GetPreserveAspectRatio()
{
return &mPreserveAspectRatio;
}
bool
SVGSVGElement::HasViewBoxRect() const
{
SVGViewElement* viewElement = GetCurrentViewElement();
if ((viewElement && viewElement->mViewBox.HasRect()) ||
(mSVGView && mSVGView->mViewBox.HasRect())) {
return true;
}
return mViewBox.HasRect();
}
bool
SVGSVGElement::ShouldSynthesizeViewBox() const
{
MOZ_ASSERT(!HasViewBoxRect(),
"Should only be called if we lack a viewBox");
nsIDocument* doc = GetUncomposedDoc();
return doc &&
doc->IsBeingUsedAsImage() &&
!mIsPaintingSVGImageElement &&
!GetParent();
}
bool
SVGSVGElement::SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR)
{
SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio,
pAROverridePtr,
nsINode::DeleteProperty<SVGPreserveAspectRatio>,
true);
MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
"Setting override value when it's already set...?");
if (MOZ_UNLIKELY(NS_FAILED(rv))) {
// property-insertion failed (e.g. OOM in property-table code)
delete pAROverridePtr;
return false;
}
return true;
}
const SVGPreserveAspectRatio*
SVGSVGElement::GetPreserveAspectRatioProperty() const
{
void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
if (valPtr) {
return static_cast<SVGPreserveAspectRatio*>(valPtr);
}
return nullptr;
}
bool
SVGSVGElement::ClearPreserveAspectRatioProperty()
{
void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio);
delete static_cast<SVGPreserveAspectRatio*>(valPtr);
return valPtr;
}
void
SVGSVGElement::
SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR)
{
#ifdef DEBUG
MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
"should only override preserveAspectRatio in images");
#endif
bool hasViewBoxRect = HasViewBoxRect();
if (!hasViewBoxRect && ShouldSynthesizeViewBox()) {
// My non-<svg:image> clients will have been painting me with a synthesized
// viewBox, but my <svg:image> client that's about to paint me now does NOT
// want that. Need to tell ourselves to flush our transform.
mImageNeedsTransformInvalidation = true;
}
mIsPaintingSVGImageElement = true;
if (!hasViewBoxRect) {
return; // preserveAspectRatio irrelevant (only matters if we have viewBox)
}
if (aPAR.GetDefer() && HasPreserveAspectRatio()) {
return; // Referring element defers to my own preserveAspectRatio value.
}
if (SetPreserveAspectRatioProperty(aPAR)) {
mImageNeedsTransformInvalidation = true;
}
}
void
SVGSVGElement::ClearImageOverridePreserveAspectRatio()
{
#ifdef DEBUG
MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
"should only override image preserveAspectRatio in images");
#endif
mIsPaintingSVGImageElement = false;
if (!HasViewBoxRect() && ShouldSynthesizeViewBox()) {
// My non-<svg:image> clients will want to paint me with a synthesized
// viewBox, but my <svg:image> client that just painted me did NOT
// use that. Need to tell ourselves to flush our transform.
mImageNeedsTransformInvalidation = true;
}
if (ClearPreserveAspectRatioProperty()) {
mImageNeedsTransformInvalidation = true;
}
}
void
SVGSVGElement::FlushImageTransformInvalidation()
{
MOZ_ASSERT(!GetParent(), "Should only be called on root node");
MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
"Should only be called on image documents");
if (mImageNeedsTransformInvalidation) {
InvalidateTransformNotifyFrame();
mImageNeedsTransformInvalidation = false;
}
}
int32_t
SVGSVGElement::GetIntrinsicWidth()
{
if (mLengthAttributes[ATTR_WIDTH].IsPercentage()) {
return -1;
}
// Passing |this| as a SVGSVGElement* invokes the variant of GetAnimValue
// that uses the passed argument as the context, but that's fine since we
// know the length isn't a percentage so the context won't be used (and we
// need to pass the element to be able to resolve em/ex units).
float width = mLengthAttributes[ATTR_WIDTH].GetAnimValue(this);
return nsSVGUtils::ClampToInt(width);
}
int32_t
SVGSVGElement::GetIntrinsicHeight()
{
if (mLengthAttributes[ATTR_HEIGHT].IsPercentage()) {
return -1;
}
// Passing |this| as a SVGSVGElement* invokes the variant of GetAnimValue
// that uses the passed argument as the context, but that's fine since we
// know the length isn't a percentage so the context won't be used (and we
// need to pass the element to be able to resolve em/ex units).
float height = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(this);
return nsSVGUtils::ClampToInt(height);
}
} // namespace dom
} // namespace mozilla