Source/JavaScriptCore/inspector/InspectorValues.h

@@public:
274274
275275 unsigned length() const { return static_cast<unsigned>(m_map.size()); }
276276
 277 RefPtr<InspectorValue> get(size_t index) const;
 278
277279 size_t memoryCost() const final;
278280
279281protected:

@@protected:
289291 void pushObject(RefPtr<InspectorObjectBase>&&);
290292 void pushArray(RefPtr<InspectorArrayBase>&&);
291293
292  RefPtr<InspectorValue> get(size_t index) const;
293 
294294 void writeJSON(StringBuilder& output) const override;
295295
296296 iterator begin() { return m_map.begin(); }

Source/JavaScriptCore/inspector/ScriptCallFrame.h

@@public:
5252 bool isEqual(const ScriptCallFrame&) const;
5353 bool isNative() const;
5454
 55 bool operator==(const ScriptCallFrame& o) const { return isEqual(o); }
 56
5557 Ref<Inspector::Protocol::Console::CallFrame> buildInspectorObject() const;
5658
5759private:

Source/JavaScriptCore/inspector/protocol/Canvas.json

4141 { "name": "contextAttributes", "$ref": "ContextAttributes", "optional": true, "description": "Context attributes for WebGL rendering contexts." },
4242 { "name": "memoryCost", "type": "number", "optional": true, "description": "Memory usage of the canvas in bytes." }
4343 ]
 44 },
 45 {
 46 "id": "Recording",
 47 "type": "object",
 48 "properties": [
 49 { "name": "version", "type": "integer" },
 50 { "name": "type", "$ref": "ContextType" },
 51 { "name": "initialState", "$ref": "RecordingInitialState", "description": "JSON data of inital state of canvas before recording." },
 52 { "name": "frames", "type": "array", "items": { "$ref": "RecordingFrame" }, "description": "JSON data of all canvas API calls." },
 53 { "name": "data", "type": "array", "items": { "type": "any" }, "description": "Array of objects that can be referenced by index. Used to avoid duplicating objects." }
 54 ]
 55 },
 56 {
 57 "id": "RecordingInitialState",
 58 "type": "object",
 59 "description": "Information about the initial state of the recorded canvas.",
 60 "properties": [
 61 { "name": "attributes", "type": "object", "optional": true, "description": "Key-value map for each attribute of the state." },
 62 { "name": "parameters", "type": "array", "items": { "type": "any" }, "optional": true, "description": "Array of values that were used to construct the recorded object." },
 63 { "name": "content", "type": "string", "optional": true, "description": "Current content at the start of the recording." }
 64 ]
 65 },
 66 {
 67 "id": "RecordingFrame",
 68 "type": "object",
 69 "description": "Container object for a single frame of the recording.",
 70 "properties": [
 71 { "name": "actions", "type": "array", "items": { "type": "any" }, "description": "Information about an action made to the recorded canvas. Follows the structure [name, parameters, trace], where name is a string, parameters is an array, and trace is an array."},
 72 { "name": "incomplete", "type": "boolean", "optional": true, "description": "Flag indicating if recording was stopped before frame ended." }
 73 ]
4474 }
4575 ],
4676 "commands": [

92122 "returns": [
93123 { "name": "object", "$ref": "Runtime.RemoteObject", "description": "JavaScript object wrapper for given canvas context." }
94124 ]
 125 },
 126 {
 127 "name": "requestRecording",
 128 "description": "Requests that the next frame or up to the given number of bytes of data be recorded for the given canvas.",
 129 "parameters": [
 130 { "name": "canvasId", "$ref": "CanvasId" },
 131 { "name": "singleFrame", "type": "boolean", "optional": true, "description": "Whether to record a single frame or until the memory limit is reached." },
 132 { "name": "memoryLimit", "type": "integer", "optional": true, "description": "Memory limit of recorded data." }
 133 ]
 134 },
 135 {
 136 "name": "cancelRecording",
 137 "description": "Cancels a requested recording for the given canvas.",
 138 "parameters": [
 139 { "name": "canvasId", "$ref": "CanvasId" }
 140 ]
95141 }
96142 ],
97143 "events": [

119165 "parameters": [
120166 { "name": "canvasId", "$ref": "CanvasId", "description": "Identifier of canvas that changed." }
121167 ]
 168 },
 169 {
 170 "name": "recordingFinished",
 171 "parameters": [
 172 { "name": "canvasId", "$ref": "CanvasId" },
 173 { "name": "recording", "$ref": "Recording" }
 174 ]
122175 }
123176 ]
124177}

Source/JavaScriptCore/interpreter/StackVisitor.cpp

@@void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin
225225}
226226#endif // ENABLE(DFG_JIT)
227227
228 bool StackVisitor::Frame::isWasmFrame() const
229 {
230  return m_isWasmFrame;
231 }
232 
233228StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const
234229{
235230 if (isWasmFrame())

Source/JavaScriptCore/interpreter/StackVisitor.h

@@public:
7777
7878 bool isNativeFrame() const { return !codeBlock() && !isWasmFrame(); }
7979 bool isInlinedFrame() const { return !!inlineCallFrame(); }
80  bool isWasmFrame() const;
 80 bool isWasmFrame() const { return m_isWasmFrame; }
8181 Wasm::IndexOrName const wasmFunctionIndexOrName()
8282 {
8383 ASSERT(isWasmFrame());

@@public:
8888 JS_EXPORT_PRIVATE String sourceURL() const;
8989 JS_EXPORT_PRIVATE String toString() const;
9090
91  intptr_t sourceID();
 91 JS_EXPORT_PRIVATE intptr_t sourceID();
9292
9393 CodeType codeType() const;
9494 bool hasLineAndColumnInfo() const;

Source/WebCore/WebCore.xcodeproj/project.pbxproj

28662866 69A6CBAD1C6BE42C00B836E9 /* AccessibilitySVGElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 697101081C6BE1550018C7F1 /* AccessibilitySVGElement.h */; };
28672867 6A22E8701F10418600F546C3 /* InspectorCanvas.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A22E86F1F10418600F546C3 /* InspectorCanvas.h */; };
28682868 6A22E8731F1042C400F546C3 /* InspectorCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A22E8721F1042C400F546C3 /* InspectorCanvas.cpp */; };
 2869 9540D31F1EFCA78D00DA2EBB /* InspectorCanvasActionTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 9540D31E1EFCA76E00DA2EBB /* InspectorCanvasActionTypes.h */; };
28692870 6A32D7CE1A16D8C000412F0B /* InspectorCanvasAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A4B6D6619D225D8006F11D3 /* InspectorCanvasAgent.cpp */; };
28702871 6A4B6D6519D22519006F11D3 /* InspectorCanvasAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A4B6D6419D22519006F11D3 /* InspectorCanvasAgent.h */; };
28712872 6B3480940EEF50D400AC1B41 /* NativeImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B3480920EEF50D400AC1B41 /* NativeImage.h */; settings = {ATTRIBUTES = (Private, ); }; };

1065210653 697101081C6BE1550018C7F1 /* AccessibilitySVGElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilitySVGElement.h; sourceTree = "<group>"; };
1065310654 6A22E86F1F10418600F546C3 /* InspectorCanvas.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InspectorCanvas.h; sourceTree = "<group>"; };
1065410655 6A22E8721F1042C400F546C3 /* InspectorCanvas.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorCanvas.cpp; sourceTree = "<group>"; };
 10656 9540D31E1EFCA76E00DA2EBB /* InspectorCanvasActionTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InspectorCanvasActionTypes.h; sourceTree = "<group>"; };
1065510657 6A4B6D6419D22519006F11D3 /* InspectorCanvasAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorCanvasAgent.h; sourceTree = "<group>"; };
1065610658 6A4B6D6619D225D8006F11D3 /* InspectorCanvasAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorCanvasAgent.cpp; sourceTree = "<group>"; };
1065710659 6B3480920EEF50D400AC1B41 /* NativeImage.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NativeImage.h; sourceTree = "<group>"; };

1711217114 B885E8D311E06DD2009FFBF4 /* InspectorApplicationCacheAgent.h */,
1711317115 6A22E8721F1042C400F546C3 /* InspectorCanvas.cpp */,
1711417116 6A22E86F1F10418600F546C3 /* InspectorCanvas.h */,
 17117 9540D31E1EFCA76E00DA2EBB /* InspectorCanvasActionTypes.h */,
1711517118 6A4B6D6619D225D8006F11D3 /* InspectorCanvasAgent.cpp */,
1711617119 6A4B6D6419D22519006F11D3 /* InspectorCanvasAgent.h */,
1711717120 7A1F2B51126C61B20006A7E6 /* InspectorClient.cpp */,

2757327576 93309DF2099E64920056E581 /* InsertTextCommand.h in Headers */,
2757427577 B885E8D511E06DD2009FFBF4 /* InspectorApplicationCacheAgent.h in Headers */,
2757527578 6A22E8701F10418600F546C3 /* InspectorCanvas.h in Headers */,
 27579 9540D31F1EFCA78D00DA2EBB /* InspectorCanvasActionTypes.h in Headers */,
2757627580 6A4B6D6519D22519006F11D3 /* InspectorCanvasAgent.h in Headers */,
2757727581 1C81B95C0E97330800266E07 /* InspectorClient.h in Headers */,
2757827582 1C81B95A0E97330800266E07 /* InspectorController.h in Headers */,

Source/WebCore/bindings/scripts/CodeGeneratorJS.pm

@@sub GenerateAttributeGetterBodyDefinition
46714671
46724672 my $toJSExpression = NativeToJSValueUsingReferences($attribute, $interface, "${functionName}(" . join(", ", @arguments) . ")", "*thisObject.globalObject()");
46734673 push(@$outputArray, " auto& impl = thisObject.wrapped();\n") unless $attribute->isStatic or $attribute->isMapLike;
 4674
 4675 my $inspectorRecording = $attribute->extendedAttributes->{InspectorRecording} || $interface->extendedAttributes->{InspectorRecording};
 4676 if ($inspectorRecording) {
 4677 AddToImplIncludes("InspectorInstrumentation.h");
 4678
 4679 push(@$outputArray, " if (UNLIKELY(impl.inspectorRecordingActive()))\n");
 4680 push(@$outputArray, " InspectorInstrumentation::${inspectorRecording}(impl, ASCIILiteral(\"" . $attribute->name . "\"));\n");
 4681 }
 4682
46744683 push(@$outputArray, " JSValue result = ${toJSExpression};\n");
46754684 push(@$outputArray, " thisObject.m_" . $attribute->name . ".set(state.vm(), &thisObject, result);\n") if $attribute->extendedAttributes->{CachedAttribute};
46764685 push(@$outputArray, " return result;\n");

@@sub GenerateAttributeSetterBodyDefinition
48754884 my $functionString = "${functionName}(" . join(", ", @arguments) . ")";
48764885 $functionString = "propagateException(state, throwScope, $functionString)" if $attribute->extendedAttributes->{SetterMayThrowException};
48774886
 4887 my $inspectorRecording = $attribute->extendedAttributes->{InspectorRecording} || $interface->extendedAttributes->{InspectorRecording};
 4888 if ($inspectorRecording) {
 4889 AddToImplIncludes("InspectorInstrumentation.h");
 4890
 4891 push(@$outputArray, " if (UNLIKELY(impl.inspectorRecordingActive()))\n");
 4892 push(@$outputArray, " InspectorInstrumentation::${inspectorRecording}(impl, ASCIILiteral(\"" . $attribute->name . "\"), \{ nativeValue \});\n");
 4893 }
 4894
48784895 push(@$outputArray, " ${functionString};\n");
48794896 push(@$outputArray, " return true;\n");
48804897 }

@@sub GenerateImplementationFunctionCall
60066023{
60076024 my ($outputArray, $operation, $interface, $functionString, $indent) = @_;
60086025
 6026 my $inspectorRecording = $operation->extendedAttributes->{InspectorRecording} || $interface->extendedAttributes->{InspectorRecording};
 6027 if ($inspectorRecording) {
 6028 AddToImplIncludes("InspectorInstrumentation.h");
 6029
 6030 my @inspectorRecordingArguments = ();
 6031 foreach my $argument (@{$operation->arguments}) {
 6032 push(@inspectorRecordingArguments, $argument->name);
 6033 }
 6034
 6035 push(@$outputArray, $indent . "if (UNLIKELY(impl.inspectorRecordingActive()))\n");
 6036 push(@$outputArray, $indent . " InspectorInstrumentation::${inspectorRecording}(impl, ASCIILiteral(\"" . $operation->name . "\"), \{ " . join(", ", @inspectorRecordingArguments) . " \});\n");
 6037 }
 6038
60096039 if (OperationHasForcedReturnValue($operation)) {
60106040 push(@$outputArray, $indent . "$functionString;\n");
60116041 push(@$outputArray, $indent . "return JSValue::encode(returnValue);\n");

Source/WebCore/bindings/scripts/IDLAttributes.json

209209 "url": "https://heycam.github.io/webidl/#Global"
210210 }
211211 },
 212 "InspectorRecording": {
 213 "contextsAllowed": ["interface", "attribute", "operation"],
 214 "values": ["*"]
 215 },
212216 "ImplementedAs": {
213217 "contextsAllowed": ["interface", "dictionary", "dictionary-member", "enum", "attribute", "operation"],
214218 "values": ["*"]

Source/WebCore/html/HTMLCanvasElement.cpp

@@bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
463463
464464void HTMLCanvasElement::paint(GraphicsContext& context, const LayoutRect& r)
465465{
 466 if (m_context && UNLIKELY(m_context->inspectorRecordingActive()))
 467 InspectorInstrumentation::didFinishRecordingCanvasFrame(*this);
 468
466469 // Clear the dirty rect
467470 m_dirtyRect = FloatRect();
468471

Source/WebCore/html/canvas/CanvasRenderingContext.h

@@public:
6262 virtual void paintRenderingResultsToCanvas() {}
6363 virtual PlatformLayer* platformLayer() const { return 0; }
6464
 65 bool inspectorRecordingActive() const { return m_inspectorRecordingActive; }
 66 void setInspectorRecordingActive(bool inspectorRecordingActive) { m_inspectorRecordingActive = inspectorRecordingActive; }
 67
6568protected:
6669 CanvasRenderingContext(HTMLCanvasElement&);
6770 bool wouldTaintOrigin(const CanvasPattern*);

@@protected:
7780 }
7881 void checkOrigin(const URL&);
7982
 83 bool m_inspectorRecordingActive { false };
 84
8085private:
8186 HTMLCanvasElement& m_canvas;
8287};

Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp

@@static WindRule toWindRule(CanvasRenderingContext2D::WindingRule rule)
965965 return rule == CanvasRenderingContext2D::WindingRule::Nonzero ? RULE_NONZERO : RULE_EVENODD;
966966}
967967
 968String CanvasRenderingContext2D::stringForWindingRule(WindingRule windingRule)
 969{
 970 switch (windingRule) {
 971 case WindingRule::Nonzero:
 972 return ASCIILiteral("nonzero");
 973 case WindingRule::Evenodd:
 974 return ASCIILiteral("evenodd");
 975 }
 976
 977 ASSERT_NOT_REACHED();
 978 return String();
 979}
 980
968981void CanvasRenderingContext2D::fill(WindingRule windingRule)
969982{
970983 fillInternal(m_path, windingRule);

@@static inline InterpolationQuality smoothingToInterpolationQuality(CanvasRenderi
25582571 return InterpolationLow;
25592572};
25602573
 2574String CanvasRenderingContext2D::stringForImageSmoothingQuality(ImageSmoothingQuality imageSmoothingQuality)
 2575{
 2576 switch (imageSmoothingQuality) {
 2577 case ImageSmoothingQuality::Low:
 2578 return ASCIILiteral("low");
 2579 case ImageSmoothingQuality::Medium:
 2580 return ASCIILiteral("medium");
 2581 case ImageSmoothingQuality::High:
 2582 return ASCIILiteral("high");
 2583 }
 2584
 2585 ASSERT_NOT_REACHED();
 2586 return String();
 2587}
 2588
25612589auto CanvasRenderingContext2D::imageSmoothingQuality() const -> ImageSmoothingQuality
25622590{
25632591 return state().imageSmoothingQuality;

Source/WebCore/html/canvas/CanvasRenderingContext2D.h

@@public:
135135 void beginPath();
136136
137137 enum class WindingRule { Nonzero, Evenodd };
 138 static String stringForWindingRule(WindingRule);
138139
139140 void fill(WindingRule = WindingRule::Nonzero);
140141 void stroke();

@@public:
220221 void setImageSmoothingEnabled(bool);
221222
222223 enum class ImageSmoothingQuality { Low, Medium, High };
 224 static String stringForImageSmoothingQuality(ImageSmoothingQuality);
 225
223226 ImageSmoothingQuality imageSmoothingQuality() const;
224227 void setImageSmoothingQuality(ImageSmoothingQuality);
225228

@@public:
232235 String displayListAsText(DisplayList::AsTextFlags) const;
233236 String replayDisplayListAsText(DisplayList::AsTextFlags) const;
234237
235 private:
236238 enum class Direction {
237239 Inherit,
238240 RTL,

@@private:
296298 FontProxy font;
297299 };
298300
 301 const State& state() const { return m_stateStack.last(); }
 302
 303private:
299304 enum CanvasDidDrawOption {
300305 CanvasDidDrawApplyNone = 0,
301306 CanvasDidDrawApplyTransform = 1,

@@private:
305310 };
306311
307312 State& modifiableState() { ASSERT(!m_unrealizedSaveCount || m_stateStack.size() >= MaxSaveCount); return m_stateStack.last(); }
308  const State& state() const { return m_stateStack.last(); }
309313
310314 void applyLineDash() const;
311315 void setShadow(const FloatSize& offset, float blur, const Color&);

Source/WebCore/html/canvas/CanvasRenderingContext2D.idl

@@typedef (HTMLImageElement or HTMLCanvasElement) CanvasImageSource;
3737 CustomIsReachable,
3838 JSGenerateToJSObject,
3939 JSCustomMarkFunction,
 40 InspectorRecording=recordCanvasAction,
4041] interface CanvasRenderingContext2D {
4142
4243 // back-reference to the canvas

Source/WebCore/inspector/InspectorCanvas.cpp

2626#include "config.h"
2727#include "InspectorCanvas.h"
2828
 29#include "CachedImage.h"
 30#include "CanvasGradient.h"
 31#include "CanvasPattern.h"
2932#include "CanvasRenderingContext2D.h"
3033#include "Document.h"
 34#include "FloatPoint.h"
3135#include "Frame.h"
 36#include "Gradient.h"
3237#include "HTMLCanvasElement.h"
 38#include "HTMLImageElement.h"
 39#include "HTMLVideoElement.h"
 40#include "Image.h"
 41#include "ImageBuffer.h"
 42#include "ImageData.h"
3343#include "InspectorDOMAgent.h"
3444#include "InspectorPageAgent.h"
3545#include "InstrumentingAgents.h"
 46#include "Pattern.h"
 47#include "StringAdaptors.h"
3648#if ENABLE(WEBGL)
3749#include "WebGLRenderingContext.h"
3850#endif

@@InspectorCanvas::InspectorCanvas(HTMLCanvasElement& canvas, const String& cssCan
6072{
6173}
6274
 75InspectorCanvas::~InspectorCanvas()
 76{
 77 resetRecordingData();
 78}
 79
 80void InspectorCanvas::resetRecordingData()
 81{
 82 m_initialState = nullptr;
 83 m_frames = nullptr;
 84 m_currentActions = nullptr;
 85 m_serializedDuplicateData = nullptr;
 86 m_indexedDuplicateData.clear();
 87 m_availableBufferSpace = 100 * 1024 * 1024;
 88 m_singleFrame = true;
 89
 90 m_canvas.renderingContext()->setInspectorRecordingActive(false);
 91}
 92
 93bool InspectorCanvas::hasRecordingData() const
 94{
 95 return m_initialState && m_frames;
 96}
 97
 98void InspectorCanvas::setInitialState(RefPtr<Inspector::Protocol::Canvas::RecordingInitialState>&& initialState)
 99{
 100 ASSERT(!hasRecordingData());
 101 if (hasRecordingData())
 102 return;
 103
 104 m_availableBufferSpace -= initialState->memoryCost();
 105 m_initialState = WTFMove(initialState);
 106
 107 m_frames = Inspector::Protocol::Array<Inspector::Protocol::Canvas::RecordingFrame>::create();
 108}
 109
 110void InspectorCanvas::recordAction(RefPtr<Inspector::Protocol::Array<InspectorValue>>&& recordingAction)
 111{
 112 ASSERT(hasRecordingData());
 113 if (!hasRecordingData())
 114 return;
 115
 116 if (!m_currentActions) {
 117 m_currentActions = Inspector::Protocol::Array<InspectorValue>::create();
 118
 119 auto frame = Inspector::Protocol::Canvas::RecordingFrame::create()
 120 .setActions(m_currentActions)
 121 .release();
 122
 123 m_frames->addItem(WTFMove(frame));
 124 }
 125
 126 m_availableBufferSpace -= recordingAction->memoryCost();
 127 m_currentActions->addItem(WTFMove(recordingAction));
 128}
 129
 130RefPtr<Inspector::Protocol::Array<InspectorValue>> InspectorCanvas::buildArrayForCanvasGradient(const CanvasGradient& canvasGradient)
 131{
 132 const Gradient& gradient = canvasGradient.gradient();
 133 bool isRadial = gradient.isRadial();
 134
 135 String type = isRadial ? ASCIILiteral("radial-gradient") : ASCIILiteral("linear-gradient");
 136
 137 RefPtr<Inspector::Protocol::Array<float>> parameters = Inspector::Protocol::Array<float>::create();
 138 parameters->addItem(gradient.p0().x());
 139 parameters->addItem(gradient.p0().y());
 140 if (isRadial)
 141 parameters->addItem(gradient.startRadius());
 142 parameters->addItem(gradient.p1().x());
 143 parameters->addItem(gradient.p1().y());
 144 if (isRadial)
 145 parameters->addItem(gradient.endRadius());
 146
 147 RefPtr<Inspector::Protocol::Array<InspectorValue>> stops = Inspector::Protocol::Array<InspectorValue>::create();
 148 for (const Gradient::ColorStop& colorStop : gradient.stops()) {
 149 RefPtr<Inspector::Protocol::Array<InspectorValue>> stop = Inspector::Protocol::Array<InspectorValue>::create();
 150 stop->addItem(colorStop.offset);
 151 stop->addItem(static_cast<double>(indexForData(colorStop.color.cssText())));
 152 stops->addItem(WTFMove(stop));
 153 }
 154
 155 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> array = Inspector::Protocol::Array<Inspector::InspectorValue>::create();
 156 array->addItem(static_cast<double>(indexForData(type)));
 157 array->addItem(WTFMove(parameters));
 158 array->addItem(WTFMove(stops));
 159 return array;
 160}
 161
 162RefPtr<Inspector::Protocol::Array<InspectorValue>> InspectorCanvas::buildArrayForCanvasPattern(const CanvasPattern& canvasPattern)
 163{
 164 Image& tileImage = canvasPattern.pattern().tileImage();
 165 std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(tileImage.size(), RenderingMode::Unaccelerated);
 166 imageBuffer->context().drawImage(tileImage, FloatPoint(0, 0));
 167
 168 String repeat;
 169 bool repeatX = canvasPattern.pattern().repeatX();
 170 bool repeatY = canvasPattern.pattern().repeatY();
 171 if (repeatX && repeatY)
 172 repeat = ASCIILiteral("repeat");
 173 else if (repeatX && !repeatY)
 174 repeat = ASCIILiteral("repeat-x");
 175 else if (!repeatX && repeatY)
 176 repeat = ASCIILiteral("repeat-y");
 177 else
 178 repeat = ASCIILiteral("no-repeat");
 179
 180 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> array = Inspector::Protocol::Array<Inspector::InspectorValue>::create();
 181 array->addItem(static_cast<double>(indexForData("pattern")));
 182 array->addItem(static_cast<double>(indexForData(imageBuffer->toDataURL("image/png"))));
 183 array->addItem(static_cast<double>(indexForData(repeat)));
 184 return array;
 185}
 186
 187RefPtr<Inspector::Protocol::Array<InspectorValue>> InspectorCanvas::buildArrayForImageData(const ImageData& imageData)
 188{
 189 RefPtr<Inspector::Protocol::Array<int>> data = Inspector::Protocol::Array<int>::create();
 190 for (size_t i = 0; i < imageData.data()->length(); ++i)
 191 data->addItem(imageData.data()->item(i));
 192
 193 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> array = Inspector::Protocol::Array<Inspector::InspectorValue>::create();
 194 array->addItem(WTFMove(data));
 195 array->addItem(imageData.width());
 196 array->addItem(imageData.height());
 197 return array;
 198}
 199
 200size_t InspectorCanvas::indexForData(DuplicateDataVariant data)
 201{
 202 size_t index = m_indexedDuplicateData.find(data);
 203 if (index != notFound)
 204 return index;
 205
 206 if (!m_serializedDuplicateData)
 207 m_serializedDuplicateData = Inspector::Protocol::Array<InspectorValue>::create();
 208
 209 RefPtr<InspectorValue> item;
 210 WTF::switchOn(data,
 211 [&] (const HTMLImageElement* imageElement) {
 212 String dataURL = ASCIILiteral("data:,");
 213
 214 if (CachedImage* cachedImage = imageElement->cachedImage()) {
 215 Image* image = cachedImage->image();
 216 if (image && image != &Image::nullImage()) {
 217 std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(image->size(), RenderingMode::Unaccelerated);
 218 imageBuffer->context().drawImage(*image, FloatPoint(0, 0));
 219 dataURL = imageBuffer->toDataURL("image/png");
 220 }
 221 }
 222
 223 item = InspectorValue::create(dataURL);
 224 },
 225#if ENABLE(VIDEO)
 226 [&] (HTMLVideoElement* videoElement) {
 227 String dataURL = ASCIILiteral("data:,");
 228
 229 unsigned videoWidth = videoElement->videoWidth();
 230 unsigned videoHeight = videoElement->videoHeight();
 231 std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(FloatSize(videoWidth, videoHeight), RenderingMode::Unaccelerated);
 232 if (imageBuffer) {
 233 videoElement->paintCurrentFrameInContext(imageBuffer->context(), FloatRect(0, 0, videoWidth, videoHeight));
 234 dataURL = imageBuffer->toDataURL("image/png");
 235 }
 236
 237 item = InspectorValue::create(dataURL);
 238 },
 239#endif
 240 [&] (HTMLCanvasElement* canvasElement) {
 241 String dataURL = ASCIILiteral("data:,");
 242
 243 ExceptionOr<UncachedString> result = canvasElement->toDataURL(ASCIILiteral("image/png"));
 244 if (!result.hasException())
 245 dataURL = result.releaseReturnValue().string;
 246
 247 item = InspectorValue::create(dataURL);
 248 },
 249 [&] (const CanvasGradient* canvasGradient) { item = buildArrayForCanvasGradient(*canvasGradient); },
 250 [&] (const CanvasPattern* canvasPattern) { item = buildArrayForCanvasPattern(*canvasPattern); },
 251 [&] (const ImageData* imageData) { item = buildArrayForImageData(*imageData); },
 252 [&] (const ScriptCallFrame& scriptCallFrame) {
 253 auto array = Inspector::Protocol::Array<double>::create();
 254 array->addItem(static_cast<double>(indexForData(scriptCallFrame.functionName())));
 255 array->addItem(static_cast<double>(indexForData(scriptCallFrame.sourceURL())));
 256 array->addItem(static_cast<double>(scriptCallFrame.lineNumber()));
 257 array->addItem(static_cast<double>(scriptCallFrame.columnNumber()));
 258 item = WTFMove(array);
 259 },
 260 [&] (const String& value) { item = InspectorValue::create(value); }
 261 );
 262
 263 m_availableBufferSpace -= item->memoryCost();
 264 m_serializedDuplicateData->addItem(WTFMove(item));
 265
 266 m_indexedDuplicateData.append(data);
 267 return m_indexedDuplicateData.size() - 1;
 268}
 269
 270RefPtr<Inspector::Protocol::Array<InspectorValue>>&& InspectorCanvas::releaseData()
 271{
 272 m_indexedDuplicateData.clear();
 273 return WTFMove(m_serializedDuplicateData);
 274}
 275
 276void InspectorCanvas::markNewFrame()
 277{
 278 m_currentActions = nullptr;
 279}
 280
 281void InspectorCanvas::markCurrentFrameIncomplete()
 282{
 283 if (!m_currentActions)
 284 return;
 285
 286 static_cast<Inspector::Protocol::Canvas::RecordingFrame*>(m_frames->get(m_frames->length() - 1).get())->setIncomplete(true);
 287}
 288
 289Inspector::Protocol::Canvas::ContextType InspectorCanvas::contextType() const
 290{
 291 CanvasRenderingContext* context = m_canvas.renderingContext();
 292
 293 if (is<CanvasRenderingContext2D>(context))
 294 return Inspector::Protocol::Canvas::ContextType::Canvas2D;
 295
 296#if ENABLE(WEBGL)
 297 if (is<WebGLRenderingContext>(context))
 298 return Inspector::Protocol::Canvas::ContextType::WebGL;
 299#endif
 300
 301#if ENABLE(WEBGL2)
 302 if (is<WebGL2RenderingContext>(context))
 303 return Inspector::Protocol::Canvas::ContextType::WebGL2;
 304#endif
 305
 306#if ENABLE(WEBGPU)
 307 if (is<WebGPURenderingContext>(context))
 308 return Inspector::Protocol::Canvas::ContextType::WebGPU;
 309#endif
 310
 311 ASSERT_NOT_REACHED();
 312 return Inspector::Protocol::Canvas::ContextType::Canvas2D;
 313}
 314
63315Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvas::buildObjectForCanvas(InstrumentingAgents& instrumentingAgents)
64316{
65317 Document& document = m_canvas.document();
66318 Frame* frame = document.frame();
67319 CanvasRenderingContext* context = m_canvas.renderingContext();
68320
69  Inspector::Protocol::Canvas::ContextType contextType;
70  if (is<CanvasRenderingContext2D>(context))
71  contextType = Inspector::Protocol::Canvas::ContextType::Canvas2D;
72 #if ENABLE(WEBGL)
73  else if (is<WebGLRenderingContext>(context))
74  contextType = Inspector::Protocol::Canvas::ContextType::WebGL;
75 #endif
76 #if ENABLE(WEBGL2)
77  else if (is<WebGL2RenderingContext>(context))
78  contextType = Inspector::Protocol::Canvas::ContextType::WebGL2;
79 #endif
80 #if ENABLE(WEBGPU)
81  else if (is<WebGPURenderingContext>(context))
82  contextType = Inspector::Protocol::Canvas::ContextType::WebGPU;
83 #endif
84  else {
85  ASSERT_NOT_REACHED();
86  contextType = Inspector::Protocol::Canvas::ContextType::Canvas2D;
87  }
88 
89321 auto canvas = Inspector::Protocol::Canvas::Canvas::create()
90322 .setCanvasId(m_identifier)
91323 .setFrameId(instrumentingAgents.inspectorPageAgent()->frameId(frame))
92  .setContextType(contextType)
 324 .setContextType(contextType())
93325 .release();
94326
95327 if (!m_cssCanvasName.isEmpty())

Source/WebCore/inspector/InspectorCanvas.h

2525
2626#pragma once
2727
 28#include "InspectorCanvasActionTypes.h"
2829#include <inspector/InspectorProtocolObjects.h>
2930#include <inspector/InspectorValues.h>
 31#include <inspector/ScriptCallFrame.h>
3032#include <wtf/HashMap.h>
 33#include <wtf/Ref.h>
 34#include <wtf/RefPtr.h>
 35#include <wtf/Variant.h>
 36#include <wtf/Vector.h>
 37#include <wtf/text/WTFString.h>
3138
3239namespace WebCore {
3340
 41class CanvasGradient;
 42class CanvasPattern;
3443class HTMLCanvasElement;
 44class HTMLImageElement;
 45class HTMLVideoElement;
 46class ImageData;
3547class InstrumentingAgents;
3648
3749typedef String ErrorString;
3850
 51typedef Variant<
 52 const HTMLImageElement*,
 53#if ENABLE(VIDEO)
 54 HTMLVideoElement*,
 55#endif
 56 HTMLCanvasElement*,
 57 const CanvasGradient*,
 58 const CanvasPattern*,
 59 const ImageData*,
 60 Inspector::ScriptCallFrame,
 61 String
 62> DuplicateDataVariant;
 63
3964class InspectorCanvas final : public RefCounted<InspectorCanvas> {
4065public:
4166 static Ref<InspectorCanvas> create(HTMLCanvasElement&, const String& cssCanvasName);

@@public:
4469 HTMLCanvasElement& canvas() { return m_canvas; }
4570 const String& cssCanvasName() { return m_cssCanvasName; }
4671
 72 void resetRecordingData();
 73 bool hasRecordingData() const;
 74
 75 void setInitialState(RefPtr<Inspector::Protocol::Canvas::RecordingInitialState>&&);
 76 void recordAction(RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>>&&);
 77 size_t indexForData(DuplicateDataVariant);
 78
 79 RefPtr<Inspector::Protocol::Canvas::RecordingInitialState>&& releaseInitialState() { return WTFMove(m_initialState); }
 80 RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Canvas::RecordingFrame>>&& releaseFrames() { return WTFMove(m_frames); }
 81 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>>&& releaseData();
 82
 83 void markNewFrame();
 84 void markCurrentFrameIncomplete();
 85
 86 void setAvailableBufferSpace(long memoryLimit) { m_availableBufferSpace = memoryLimit; }
 87 bool hasAvailableBufferSpace() const { return m_availableBufferSpace > 0; }
 88
 89 bool singleFrame() const { return m_singleFrame; }
 90 void setSingleFrame(bool singleFrame) { m_singleFrame = singleFrame; }
 91
 92 Inspector::Protocol::Canvas::ContextType contextType() const;
4793 Ref<Inspector::Protocol::Canvas::Canvas> buildObjectForCanvas(InstrumentingAgents&);
4894
49  ~InspectorCanvas() { }
 95 ~InspectorCanvas();
5096
5197private:
5298 InspectorCanvas(HTMLCanvasElement&, const String& cssCanvasName);
5399
 100 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> buildArrayForCanvasGradient(const CanvasGradient&);
 101 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> buildArrayForCanvasPattern(const CanvasPattern&);
 102 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> buildArrayForImageData(const ImageData&);
 103
54104 String m_identifier;
55105 HTMLCanvasElement& m_canvas;
56106 String m_cssCanvasName;
 107
 108 RefPtr<Inspector::Protocol::Canvas::RecordingInitialState> m_initialState;
 109 RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Canvas::RecordingFrame>> m_frames;
 110 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> m_currentActions;
 111 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> m_serializedDuplicateData;
 112 Vector<DuplicateDataVariant> m_indexedDuplicateData;
 113 long m_availableBufferSpace { 100 * 1024 * 1024 };
 114 bool m_singleFrame { true };
57115};
58116
59117} // namespace WebCore

Source/WebCore/inspector/InspectorCanvasActionTypes.h

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26#pragma once
 27
 28#include "CanvasRenderingContext2D.h"
 29#include "DOMMatrixInit.h"
 30#include <wtf/Variant.h>
 31#include <wtf/Vector.h>
 32#include <wtf/text/WTFString.h>
 33
 34namespace WebCore {
 35
 36class DOMPath;
 37class Element;
 38class HTMLImageElement;
 39class ImageData;
 40
 41typedef Variant<
 42 Element*,
 43 HTMLImageElement*,
 44 ImageData*,
 45 DOMMatrixInit,
 46 DOMPath*,
 47 Vector<float>,
 48 String,
 49 double,
 50 float,
 51 int,
 52 bool,
 53 std::optional<float>,
 54 CanvasImageSource,
 55 CanvasRenderingContext2D::Style,
 56 CanvasRenderingContext2D::WindingRule,
 57 CanvasRenderingContext2D::ImageSmoothingQuality
 58> CanvasActionParameterVariant;
 59
 60} // namespace WebCore

Source/WebCore/inspector/InspectorCanvasAgent.cpp

2626#include "config.h"
2727#include "InspectorCanvasAgent.h"
2828
 29#include "AffineTransform.h"
 30#include "CanvasGradient.h"
 31#include "CanvasPattern.h"
 32#include "CanvasRenderingContext.h"
2933#include "CanvasRenderingContext2D.h"
 34#include "DOMMatrixInit.h"
 35#include "DOMPath.h"
3036#include "Document.h"
3137#include "Element.h"
3238#include "Frame.h"
 39#include "HTMLCanvasElement.h"
 40#include "HTMLImageElement.h"
 41#include "HTMLVideoElement.h"
 42#include "ImageData.h"
3343#include "InspectorDOMAgent.h"
3444#include "InstrumentingAgents.h"
3545#include "JSCanvasRenderingContext2D.h"
3646#include "JSMainThreadExecState.h"
3747#include "MainFrame.h"
 48#include "SVGPathUtilities.h"
3849#include "ScriptState.h"
3950#include "StringAdaptors.h"
4051#include <inspector/IdentifiersFactory.h>
4152#include <inspector/InjectedScript.h>
4253#include <inspector/InjectedScriptManager.h>
4354#include <inspector/InspectorProtocolObjects.h>
 55#include <inspector/ScriptCallFrame.h>
 56#include <interpreter/CallFrame.h>
 57#include <interpreter/StackVisitor.h>
4458#include <runtime/JSCInlines.h>
4559
4660#if ENABLE(WEBGL)

@@InspectorCanvasAgent::InspectorCanvasAgent(WebAgentContext& context)
6478 , m_frontendDispatcher(std::make_unique<Inspector::CanvasFrontendDispatcher>(context.frontendRouter))
6579 , m_backendDispatcher(Inspector::CanvasBackendDispatcher::create(context.backendDispatcher, this))
6680 , m_injectedScriptManager(context.injectedScriptManager)
67  , m_timer(*this, &InspectorCanvasAgent::canvasDestroyedTimerFired)
 81 , m_canvasDestroyedTimer(*this, &InspectorCanvasAgent::canvasDestroyedTimerFired)
 82 , m_canvasRecordingTimer(*this, &InspectorCanvasAgent::canvasRecordingTimerFired)
6883{
6984}
7085

@@void InspectorCanvasAgent::disable(ErrorString&)
99114 if (!m_enabled)
100115 return;
101116
102  if (m_timer.isActive())
103  m_timer.stop();
 117 if (m_canvasDestroyedTimer.isActive())
 118 m_canvasDestroyedTimer.stop();
104119
105120 m_removedCanvasIdentifiers.clear();
106121
 122 if (m_canvasRecordingTimer.isActive())
 123 m_canvasRecordingTimer.stop();
 124
 125 for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())
 126 inspectorCanvas->resetRecordingData();
 127
107128 m_enabled = false;
108129}
109130

@@void InspectorCanvasAgent::resolveCanvasContext(ErrorString& errorString, const
220241 result = injectedScript.wrapObject(value, objectGroupName);
221242}
222243
 244void InspectorCanvasAgent::requestRecording(ErrorString& errorString, const String& canvasId, const bool* const singleFrame, const int* const memoryLimit)
 245{
 246 auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
 247 if (!inspectorCanvas)
 248 return;
 249
 250 if (inspectorCanvas->canvas().renderingContext()->inspectorRecordingActive())
 251 return;
 252
 253 inspectorCanvas->resetRecordingData();
 254 if (singleFrame)
 255 inspectorCanvas->setSingleFrame(*singleFrame);
 256 if (memoryLimit)
 257 inspectorCanvas->setAvailableBufferSpace(*memoryLimit);
 258
 259 inspectorCanvas->canvas().renderingContext()->setInspectorRecordingActive(true);
 260}
 261
 262void InspectorCanvasAgent::cancelRecording(ErrorString& errorString, const String& canvasId)
 263{
 264 auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
 265 if (!inspectorCanvas)
 266 return;
 267
 268 if (!inspectorCanvas->canvas().renderingContext()->inspectorRecordingActive())
 269 return;
 270
 271 didFinishRecordingCanvasFrame(inspectorCanvas->canvas(), true);
 272}
 273
223274void InspectorCanvasAgent::frameNavigated(Frame& frame)
224275{
225276 if (frame.isMainFrame()) {

@@void InspectorCanvasAgent::didChangeCanvasMemory(HTMLCanvasElement& canvasElemen
287338 m_frontendDispatcher->canvasMemoryChanged(inspectorCanvas->identifier(), canvasElement.memoryCost());
288339}
289340
 341static RefPtr<Inspector::Protocol::Array<double>> buildArrayForAffineTransform(const AffineTransform& affineTransform)
 342{
 343 RefPtr<Inspector::Protocol::Array<double>> array = Inspector::Protocol::Array<double>::create();
 344 array->addItem(affineTransform.a());
 345 array->addItem(affineTransform.b());
 346 array->addItem(affineTransform.c());
 347 array->addItem(affineTransform.d());
 348 array->addItem(affineTransform.e());
 349 array->addItem(affineTransform.f());
 350 return array;
 351}
 352
 353static RefPtr<Inspector::Protocol::Array<double>> buildArrayForVector(const Vector<float>& vector)
 354{
 355 RefPtr<Inspector::Protocol::Array<double>> array = Inspector::Protocol::Array<double>::create();
 356 for (double item : vector)
 357 array->addItem(item);
 358 return array;
 359}
 360
 361void InspectorCanvasAgent::recordCanvasAction(CanvasRenderingContext& canvasRenderingContext, const String& name, Vector<CanvasActionParameterVariant>&& parameters)
 362{
 363 HTMLCanvasElement& canvasElement = canvasRenderingContext.canvas();
 364
 365 auto* inspectorCanvas = findInspectorCanvas(canvasElement);
 366 ASSERT(inspectorCanvas);
 367 if (!inspectorCanvas)
 368 return;
 369
 370 ASSERT(canvasRenderingContext.inspectorRecordingActive());
 371 if (!canvasRenderingContext.inspectorRecordingActive())
 372 return;
 373
 374 if (!inspectorCanvas->hasRecordingData()) {
 375 auto initialState = Inspector::Protocol::Canvas::RecordingInitialState::create()
 376 .release();
 377
 378 auto attributes = InspectorObject::create();
 379 attributes->setInteger(ASCIILiteral("width"), canvasElement.width());
 380 attributes->setInteger(ASCIILiteral("height"), canvasElement.height());
 381
 382 auto parameters = Inspector::Protocol::Array<InspectorValue>::create();
 383
 384 if (is<CanvasRenderingContext2D>(canvasRenderingContext)) {
 385 CanvasRenderingContext2D& context2d = downcast<CanvasRenderingContext2D>(canvasRenderingContext);
 386 const CanvasRenderingContext2D::State& state = context2d.state();
 387
 388 attributes->setArray(ASCIILiteral("setTransform"), buildArrayForAffineTransform(state.transform));
 389 attributes->setDouble(ASCIILiteral("globalAlpha"), context2d.globalAlpha());
 390 attributes->setString(ASCIILiteral("globalCompositeOperation"), context2d.globalCompositeOperation());
 391 attributes->setDouble(ASCIILiteral("lineWidth"), context2d.lineWidth());
 392 attributes->setString(ASCIILiteral("lineCap"), context2d.lineCap());
 393 attributes->setString(ASCIILiteral("lineJoin"), context2d.lineJoin());
 394 attributes->setDouble(ASCIILiteral("miterLimit"), context2d.miterLimit());
 395 attributes->setDouble(ASCIILiteral("shadowOffsetX"), context2d.shadowOffsetX());
 396 attributes->setDouble(ASCIILiteral("shadowOffsetY"), context2d.shadowOffsetY());
 397 attributes->setDouble(ASCIILiteral("shadowBlur"), context2d.shadowBlur());
 398 attributes->setString(ASCIILiteral("shadowColor"), context2d.shadowColor());
 399
 400 // The parameter to `setLineDash` is itself an array, so we need to wrap the parameters
 401 // list in an array to allow spreading.
 402 auto setLineDash = Inspector::Protocol::Array<InspectorValue>::create();
 403 setLineDash->addItem(buildArrayForVector(state.lineDash));
 404 attributes->setArray(ASCIILiteral("setLineDash"), WTFMove(setLineDash));
 405
 406 attributes->setDouble(ASCIILiteral("lineDashOffset"), context2d.lineDashOffset());
 407 attributes->setString(ASCIILiteral("font"), context2d.font());
 408 attributes->setString(ASCIILiteral("textAlign"), context2d.textAlign());
 409 attributes->setString(ASCIILiteral("textBaseline"), context2d.textBaseline());
 410 attributes->setString(ASCIILiteral("direction"), context2d.direction());
 411
 412 size_t strokeStyleIndex;
 413 if (CanvasGradient* canvasGradient = state.strokeStyle.canvasGradient())
 414 strokeStyleIndex = inspectorCanvas->indexForData(canvasGradient);
 415 else if (CanvasPattern* canvasPattern = state.strokeStyle.canvasPattern())
 416 strokeStyleIndex = inspectorCanvas->indexForData(canvasPattern);
 417 else
 418 strokeStyleIndex = inspectorCanvas->indexForData(state.strokeStyle.color());
 419 attributes->setDouble(ASCIILiteral("strokeStyle"), static_cast<double>(strokeStyleIndex));
 420
 421 size_t fillStyleIndex;
 422 if (CanvasGradient* canvasGradient = state.fillStyle.canvasGradient())
 423 fillStyleIndex = inspectorCanvas->indexForData(canvasGradient);
 424 else if (CanvasPattern* canvasPattern = state.fillStyle.canvasPattern())
 425 fillStyleIndex = inspectorCanvas->indexForData(canvasPattern);
 426 else
 427 fillStyleIndex = inspectorCanvas->indexForData(state.fillStyle.color());
 428 attributes->setDouble(ASCIILiteral("fillStyle"), static_cast<double>(strokeStyleIndex));
 429
 430 attributes->setBoolean(ASCIILiteral("imageSmoothingEnabled"), context2d.imageSmoothingEnabled());
 431 attributes->setString(ASCIILiteral("imageSmoothingQuality"), CanvasRenderingContext2D::stringForImageSmoothingQuality(context2d.imageSmoothingQuality()));
 432 }
 433#if ENABLE(WEBGL)
 434 else if (is<WebGLRenderingContextBase>(canvasRenderingContext)) {
 435 WebGLRenderingContextBase& contextWebGLBase = downcast<WebGLRenderingContextBase>(canvasRenderingContext);
 436 if (std::optional<WebGLContextAttributes> attributes = contextWebGLBase.getContextAttributes()) {
 437 RefPtr<InspectorObject> contextAttributes = InspectorObject::create();
 438 contextAttributes->setBoolean(ASCIILiteral("alpha"), attributes->alpha);
 439 contextAttributes->setBoolean(ASCIILiteral("depth"), attributes->depth);
 440 contextAttributes->setBoolean(ASCIILiteral("stencil"), attributes->stencil);
 441 contextAttributes->setBoolean(ASCIILiteral("antialias"), attributes->antialias);
 442 contextAttributes->setBoolean(ASCIILiteral("premultipliedAlpha"), attributes->premultipliedAlpha);
 443 contextAttributes->setBoolean(ASCIILiteral("preserveDrawingBuffer"), attributes->preserveDrawingBuffer);
 444 contextAttributes->setBoolean(ASCIILiteral("failIfMajorPerformanceCaveat"), attributes->failIfMajorPerformanceCaveat);
 445 parameters->addItem(WTFMove(contextAttributes));
 446 }
 447 }
 448#endif
 449
 450 // FIXME: <https://webkit.org/b/174483> Web Inspector: Record actions performed on WebGLRenderingContext
 451
 452 initialState->setAttributes(WTFMove(attributes));
 453
 454 if (parameters->length())
 455 initialState->setParameters(WTFMove(parameters));
 456
 457#if ENABLE(WEBGL)
 458 if (is<WebGLRenderingContextBase>(canvasRenderingContext))
 459 downcast<WebGLRenderingContextBase>(canvasRenderingContext).setPreventBufferClearForInspector(true);
 460#endif
 461
 462 ExceptionOr<UncachedString> result = canvasElement.toDataURL(ASCIILiteral("image/png"));
 463
 464#if ENABLE(WEBGL)
 465 if (is<WebGLRenderingContextBase>(canvasRenderingContext))
 466 downcast<WebGLRenderingContextBase>(canvasRenderingContext).setPreventBufferClearForInspector(false);
 467#endif
 468
 469 if (!result.hasException())
 470 initialState->setContent(result.releaseReturnValue().string);
 471
 472 inspectorCanvas->setInitialState(WTFMove(initialState));
 473 }
 474
 475 auto action = Inspector::Protocol::Array<InspectorValue>::create();
 476 action->addItem(static_cast<double>(inspectorCanvas->indexForData(name)));
 477
 478 RefPtr<Inspector::Protocol::Array<InspectorValue>> parametersData = Inspector::Protocol::Array<Inspector::InspectorValue>::create();
 479 for (CanvasActionParameterVariant& item : parameters) {
 480 WTF::switchOn(item,
 481 [&] (const Element*) { parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(String("element")))); },
 482 [&] (const HTMLImageElement* value) { parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(value))); },
 483 [&] (const ImageData* value) {
 484 if (value)
 485 parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(value)));
 486 },
 487 [&] (const DOMMatrixInit& value) {
 488 RefPtr<Inspector::Protocol::Array<double>> array = Inspector::Protocol::Array<double>::create();
 489 array->addItem(value.a.value_or(1));
 490 array->addItem(value.b.value_or(0));
 491 array->addItem(value.c.value_or(0));
 492 array->addItem(value.d.value_or(1));
 493 array->addItem(value.e.value_or(0));
 494 array->addItem(value.f.value_or(0));
 495 parametersData->addItem(WTFMove(array));
 496 },
 497 [&] (const DOMPath* value) {
 498 String path;
 499 buildStringFromPath(value->path(), path);
 500 parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(path)));
 501 },
 502 [&] (const Vector<float>& value) { parametersData->addItem(buildArrayForVector(value)); },
 503 [&] (const String& value) { parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(value))); },
 504 [&] (double value) { parametersData->addItem(value); },
 505 [&] (float value) { parametersData->addItem(value); },
 506 [&] (int value) { parametersData->addItem(value); },
 507 [&] (bool value) { parametersData->addItem(value); },
 508 [&] (const std::optional<float>& value) {
 509 if (value)
 510 parametersData->addItem(value.value());
 511 },
 512 [&] (CanvasImageSource& canvasImageSource) {
 513 WTF::switchOn(canvasImageSource,
 514 [&] (const RefPtr<HTMLImageElement>& value) { parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(value.get()))); },
 515#if ENABLE(VIDEO)
 516 [&] (RefPtr<HTMLVideoElement>& value) { parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(value.get()))); },
 517#endif
 518 [&] (RefPtr<HTMLCanvasElement>& value) { parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(value.get()))); }
 519 );
 520 },
 521 [&] (const CanvasRenderingContext2D::Style& style) {
 522 WTF::switchOn(style,
 523 [&] (const String& value) { parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(value))); },
 524 [&] (const RefPtr<CanvasGradient>& value) { parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(value.get()))); },
 525 [&] (const RefPtr<CanvasPattern>& value) { parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(value.get()))); }
 526 );
 527 },
 528 [&] (CanvasRenderingContext2D::WindingRule value) {
 529 String windingRule = CanvasRenderingContext2D::stringForWindingRule(value);
 530 parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(windingRule)));
 531 },
 532 [&] (CanvasRenderingContext2D::ImageSmoothingQuality value) {
 533 String imageSmoothingQuality = CanvasRenderingContext2D::stringForImageSmoothingQuality(value);
 534 parametersData->addItem(static_cast<double>(inspectorCanvas->indexForData(imageSmoothingQuality)));
 535 }
 536 );
 537 }
 538 action->addItem(WTFMove(parametersData));
 539
 540 if (JSC::CallFrame* callFrame = JSMainThreadExecState::currentState()->vm().topCallFrame) {
 541 RefPtr<Inspector::Protocol::Array<double>> trace = Inspector::Protocol::Array<double>::create();
 542
 543 callFrame->iterate([&] (JSC::StackVisitor& visitor) {
 544 // Only skip Native frames if they are the first frame (e.g. CanvasRenderingContext2D.prototype.fill).
 545 if (visitor->isNativeFrame() && !trace->length())
 546 return JSC::StackVisitor::Continue;
 547
 548 unsigned line = 0;
 549 unsigned column = 0;
 550 visitor->computeLineAndColumn(line, column);
 551
 552 ScriptCallFrame scriptCallFrame(visitor->functionName(), visitor->sourceURL(), static_cast<JSC::SourceID>(visitor->sourceID()), line, column);
 553 trace->addItem(static_cast<double>(inspectorCanvas->indexForData(scriptCallFrame)));
 554
 555 return JSC::StackVisitor::Continue;
 556 });
 557
 558 action->addItem(WTFMove(trace));
 559 }
 560
 561 inspectorCanvas->recordAction(WTFMove(action));
 562
 563 if (!m_canvasRecordingTimer.isActive())
 564 m_canvasRecordingTimer.startOneShot(0_s);
 565
 566 if (!inspectorCanvas->hasAvailableBufferSpace())
 567 didFinishRecordingCanvasFrame(canvasElement, true);
 568}
 569
 570void InspectorCanvasAgent::didFinishRecordingCanvasFrame(HTMLCanvasElement& canvasElement, bool forceDispatch)
 571{
 572 auto* inspectorCanvas = findInspectorCanvas(canvasElement);
 573 ASSERT(inspectorCanvas);
 574 if (!inspectorCanvas)
 575 return;
 576
 577 ASSERT(inspectorCanvas->canvas().renderingContext()->inspectorRecordingActive());
 578 if (!inspectorCanvas->canvas().renderingContext()->inspectorRecordingActive())
 579 return;
 580
 581 if (!forceDispatch && !inspectorCanvas->singleFrame()) {
 582 inspectorCanvas->markNewFrame();
 583 return;
 584 }
 585
 586 if (!inspectorCanvas->hasRecordingData())
 587 return;
 588
 589 if (forceDispatch)
 590 inspectorCanvas->markCurrentFrameIncomplete();
 591
 592 auto recording = Inspector::Protocol::Canvas::Recording::create()
 593 .setVersion(1)
 594 .setType(inspectorCanvas->contextType())
 595 .setInitialState(inspectorCanvas->releaseInitialState())
 596 .setFrames(inspectorCanvas->releaseFrames())
 597 .setData(inspectorCanvas->releaseData())
 598 .release();
 599
 600 m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), WTFMove(recording));
 601
 602 inspectorCanvas->resetRecordingData();
 603}
 604
290605void InspectorCanvasAgent::canvasDestroyed(HTMLCanvasElement& canvasElement)
291606{
292607 auto* inspectorCanvas = findInspectorCanvas(canvasElement);

@@void InspectorCanvasAgent::canvasDestroyed(HTMLCanvasElement& canvasElement)
303618 // the frontend from making JS allocations while the GC is still active.
304619 m_removedCanvasIdentifiers.append(identifier);
305620
306  if (!m_timer.isActive())
307  m_timer.startOneShot(0_s);
 621 if (!m_canvasDestroyedTimer.isActive())
 622 m_canvasDestroyedTimer.startOneShot(0_s);
308623}
309624
310625void InspectorCanvasAgent::canvasDestroyedTimerFired()

@@void InspectorCanvasAgent::canvasDestroyedTimerFired()
320635 m_removedCanvasIdentifiers.clear();
321636}
322637
 638void InspectorCanvasAgent::canvasRecordingTimerFired()
 639{
 640 for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
 641 if (!inspectorCanvas->canvas().renderingContext()->inspectorRecordingActive())
 642 continue;
 643
 644 didFinishRecordingCanvasFrame(inspectorCanvas->canvas());
 645 }
 646}
 647
323648void InspectorCanvasAgent::clearCanvasData()
324649{
325650 for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values())

@@void InspectorCanvasAgent::clearCanvasData()
329654 m_canvasToCSSCanvasName.clear();
330655 m_removedCanvasIdentifiers.clear();
331656
332  if (m_timer.isActive())
333  m_timer.stop();
 657 if (m_canvasRecordingTimer.isActive())
 658 m_canvasRecordingTimer.stop();
 659
 660 if (m_canvasDestroyedTimer.isActive())
 661 m_canvasDestroyedTimer.stop();
334662}
335663
336664String InspectorCanvasAgent::unbindCanvas(InspectorCanvas& inspectorCanvas)

Source/WebCore/inspector/InspectorCanvasAgent.h

2727
2828#include "HTMLCanvasElement.h"
2929#include "InspectorCanvas.h"
 30#include "InspectorCanvasActionTypes.h"
3031#include "InspectorWebAgentBase.h"
3132#include "Timer.h"
3233#include <inspector/InspectorBackendDispatchers.h>
3334#include <inspector/InspectorFrontendDispatchers.h>
3435#include <wtf/HashMap.h>
35 #include <wtf/HashSet.h>
3636#include <wtf/RefPtr.h>
3737#include <wtf/Vector.h>
3838#include <wtf/text/WTFString.h>

@@class InjectedScriptManager;
4343
4444namespace WebCore {
4545
 46class CanvasRenderingContext;
4647class WebGLRenderingContextBase;
4748
4849typedef String ErrorString;

@@public:
6566 void requestContent(ErrorString&, const String& canvasId, String* content) override;
6667 void requestCSSCanvasClientNodes(ErrorString&, const String& canvasId, RefPtr<Inspector::Protocol::Array<int>>&) override;
6768 void resolveCanvasContext(ErrorString&, const String& canvasId, const String* const objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>&) override;
 69 void requestRecording(ErrorString&, const String& canvasId, const bool* const singleFrame, const int* const memoryLimit) override;
 70 void cancelRecording(ErrorString&, const String& canvasId) override;
6871
6972 // InspectorInstrumentation
7073 void frameNavigated(Frame&);

@@public:
7275 void didChangeCSSCanvasClientNodes(HTMLCanvasElement&);
7376 void didCreateCanvasRenderingContext(HTMLCanvasElement&);
7477 void didChangeCanvasMemory(HTMLCanvasElement&);
 78 void recordCanvasAction(CanvasRenderingContext&, const String&, Vector<CanvasActionParameterVariant>&& = { });
 79 void didFinishRecordingCanvasFrame(HTMLCanvasElement&, bool forceDispatch = false);
7580
7681 // CanvasObserver
7782 void canvasChanged(HTMLCanvasElement&, const FloatRect&) override { }

@@public:
8085
8186private:
8287 void canvasDestroyedTimerFired();
 88 void canvasRecordingTimerFired();
8389 void clearCanvasData();
8490 String unbindCanvas(InspectorCanvas&);
8591 InspectorCanvas* assertInspectorCanvas(ErrorString&, const String&);

@@private:
9298 HashMap<String, RefPtr<InspectorCanvas>> m_identifierToInspectorCanvas;
9399 HashMap<HTMLCanvasElement*, String> m_canvasToCSSCanvasName;
94100 Vector<String> m_removedCanvasIdentifiers;
95  Timer m_timer;
 101 Timer m_canvasDestroyedTimer;
 102 Timer m_canvasRecordingTimer;
 103
96104 bool m_enabled { false };
97105};
98106

Source/WebCore/inspector/InspectorInstrumentation.cpp

@@void InspectorInstrumentation::didChangeCanvasMemoryImpl(InstrumentingAgents* in
994994 canvasAgent->didChangeCanvasMemory(canvasElement);
995995}
996996
 997void InspectorInstrumentation::recordCanvasActionImpl(InstrumentingAgents* instrumentingAgents, CanvasRenderingContext& canvasRenderingContext, const String& name, Vector<CanvasActionParameterVariant>&& parameters)
 998{
 999 if (InspectorCanvasAgent* canvasAgent = instrumentingAgents->inspectorCanvasAgent())
 1000 canvasAgent->recordCanvasAction(canvasRenderingContext, name, WTFMove(parameters));
 1001}
 1002
 1003void InspectorInstrumentation::didFinishRecordingCanvasFrameImpl(InstrumentingAgents* instrumentingAgents, HTMLCanvasElement& canvasElement, bool forceDispatch)
 1004{
 1005 if (InspectorCanvasAgent* canvasAgent = instrumentingAgents->inspectorCanvasAgent())
 1006 canvasAgent->didFinishRecordingCanvasFrame(canvasElement, forceDispatch);
 1007}
 1008
9971009#if ENABLE(RESOURCE_USAGE)
9981010void InspectorInstrumentation::didHandleMemoryPressureImpl(InstrumentingAgents& instrumentingAgents, Critical critical)
9991011{

Source/WebCore/inspector/InspectorInstrumentation.h

3232#pragma once
3333
3434#include "CSSSelector.h"
 35#include "CanvasRenderingContext.h"
3536#include "DocumentThreadableLoader.h"
3637#include "Element.h"
3738#include "FormData.h"
3839#include "Frame.h"
3940#include "HTMLCanvasElement.h"
4041#include "HitTestResult.h"
 42#include "InspectorCanvasActionTypes.h"
4143#include "InspectorController.h"
4244#include "InspectorInstrumentationCookie.h"
4345#include "Page.h"

@@public:
224226 static void didChangeCSSCanvasClientNodes(HTMLCanvasElement&);
225227 static void didCreateCanvasRenderingContext(HTMLCanvasElement&);
226228 static void didChangeCanvasMemory(HTMLCanvasElement&);
 229 static void recordCanvasAction(CanvasRenderingContext&, const String&, Vector<CanvasActionParameterVariant>&& = { });
 230 static void didFinishRecordingCanvasFrame(HTMLCanvasElement&, bool forceDispatch = false);
227231
228232 static void networkStateChanged(Page&);
229233 static void updateApplicationCacheStatus(Frame*);

@@private:
381385 static void didChangeCSSCanvasClientNodesImpl(InstrumentingAgents*, HTMLCanvasElement&);
382386 static void didCreateCanvasRenderingContextImpl(InstrumentingAgents*, HTMLCanvasElement&);
383387 static void didChangeCanvasMemoryImpl(InstrumentingAgents*, HTMLCanvasElement&);
 388 static void recordCanvasActionImpl(InstrumentingAgents*, CanvasRenderingContext&, const String&, Vector<CanvasActionParameterVariant>&& = { });
 389 static void didFinishRecordingCanvasFrameImpl(InstrumentingAgents*, HTMLCanvasElement&, bool forceDispatch = false);
384390
385391 static void layerTreeDidChangeImpl(InstrumentingAgents&);
386392 static void renderLayerDestroyedImpl(InstrumentingAgents&, const RenderLayer&);

@@inline void InspectorInstrumentation::didChangeCanvasMemory(HTMLCanvasElement& c
10861092 didChangeCanvasMemoryImpl(instrumentingAgents, canvasElement);
10871093}
10881094
 1095inline void InspectorInstrumentation::recordCanvasAction(CanvasRenderingContext& canvasRenderingContext, const String& name, Vector<CanvasActionParameterVariant>&& parameters)
 1096{
 1097 FAST_RETURN_IF_NO_FRONTENDS(void());
 1098 if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForDocument(&canvasRenderingContext.canvas().document()))
 1099 recordCanvasActionImpl(instrumentingAgents, canvasRenderingContext, name, WTFMove(parameters));
 1100}
 1101
 1102inline void InspectorInstrumentation::didFinishRecordingCanvasFrame(HTMLCanvasElement& canvasElement, bool forceDispatch)
 1103{
 1104 FAST_RETURN_IF_NO_FRONTENDS(void());
 1105 if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForDocument(&canvasElement.document()))
 1106 didFinishRecordingCanvasFrameImpl(instrumentingAgents, canvasElement, forceDispatch);
 1107}
 1108
10891109inline void InspectorInstrumentation::networkStateChanged(Page& page)
10901110{
10911111 FAST_RETURN_IF_NO_FRONTENDS(void());

Source/WebCore/platform/graphics/Gradient.h

@@namespace WebCore {
146146 { }
147147 };
148148
 149 const Vector<ColorStop, 2>& stops() const { return m_stops; }
 150
149151 void setStopsSorted(bool s) { m_stopsSorted = s; }
150152
151153 void setSpreadMethod(GradientSpreadMethod);

Source/WebCore/svg/SVGPathUtilities.cpp

@@bool buildPathFromString(const String& d, Path& result)
4949 return SVGPathParser::parse(source, builder);
5050}
5151
 52bool buildStringFromPath(const Path& path, String& result)
 53{
 54 if (path.isNull() || path.isEmpty())
 55 return false;
 56
 57 StringBuilder builder;
 58
 59 path.apply([&builder] (const PathElement& element) {
 60 switch (element.type) {
 61 case PathElementMoveToPoint: // The points member will contain 1 value.
 62 builder.append('M');
 63 builder.append(String::numberToStringECMAScript(element.points[0].x()));
 64 builder.append(' ');
 65 builder.append(String::numberToStringECMAScript(element.points[0].y()));
 66 break;
 67 case PathElementAddLineToPoint: // The points member will contain 1 value.
 68 builder.append('L');
 69 builder.append(String::numberToStringECMAScript(element.points[0].x()));
 70 builder.append(' ');
 71 builder.append(String::numberToStringECMAScript(element.points[0].y()));
 72 break;
 73 case PathElementAddQuadCurveToPoint: // The points member will contain 2 values.
 74 builder.append('Q');
 75 builder.append(String::numberToStringECMAScript(element.points[0].x()));
 76 builder.append(' ');
 77 builder.append(String::numberToStringECMAScript(element.points[0].y()));
 78 builder.append(',');
 79 builder.append(String::numberToStringECMAScript(element.points[1].x()));
 80 builder.append(' ');
 81 builder.append(String::numberToStringECMAScript(element.points[1].y()));
 82 break;
 83 case PathElementAddCurveToPoint: // The points member will contain 3 values.
 84 builder.append('C');
 85 builder.append(String::numberToStringECMAScript(element.points[0].x()));
 86 builder.append(' ');
 87 builder.append(String::numberToStringECMAScript(element.points[0].y()));
 88 builder.append(',');
 89 builder.append(String::numberToStringECMAScript(element.points[1].x()));
 90 builder.append(' ');
 91 builder.append(String::numberToStringECMAScript(element.points[1].y()));
 92 builder.append(',');
 93 builder.append(String::numberToStringECMAScript(element.points[2].x()));
 94 builder.append(' ');
 95 builder.append(String::numberToStringECMAScript(element.points[2].y()));
 96 break;
 97 case PathElementCloseSubpath: // The points member will contain no values.
 98 builder.append('Z');
 99 break;
 100 }
 101 });
 102
 103 result = builder.toString();
 104 return true;
 105}
 106
52107bool buildSVGPathByteStreamFromSVGPathSegListValues(const SVGPathSegListValues& list, SVGPathByteStream& result, PathParsingMode parsingMode)
53108{
54109 result.clear();

Source/WebCore/svg/SVGPathUtilities.h

@@class SVGPathSegListValues;
3636bool buildPathFromString(const String&, Path&);
3737bool buildPathFromByteStream(const SVGPathByteStream&, Path&);
3838
 39// Path -> String
 40bool buildStringFromPath(const Path&, String&);
 41
3942// SVGPathSegListValues/String -> SVGPathByteStream
4043bool buildSVGPathByteStreamFromSVGPathSegListValues(const SVGPathSegListValues&, SVGPathByteStream& result, PathParsingMode);
4144bool appendSVGPathByteStreamFromSVGPathSeg(RefPtr<SVGPathSeg>&&, SVGPathByteStream&, PathParsingMode);

Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

@@localizedStrings["Call Trees"] = "Call Trees";
149149localizedStrings["Calls"] = "Calls";
150150localizedStrings["Cancel Automatic Continue"] = "Cancel Automatic Continue";
151151localizedStrings["Cancel comparison"] = "Cancel comparison";
 152localizedStrings["Cancel recording"] = "Cancel recording";
152153localizedStrings["Canvas"] = "Canvas";
153154localizedStrings["Canvas %d"] = "Canvas %d";
154155localizedStrings["Canvas %s"] = "Canvas %s";

@@localizedStrings["Catch Variables"] = "Catch Variables";
161162localizedStrings["Categories"] = "Categories";
162163localizedStrings["Center X"] = "Center X";
163164localizedStrings["Center Y"] = "Center Y";
 165localizedStrings["Changes will take effect on the next load"] = "Changes will take effect on the next load";
164166localizedStrings["Character Data"] = "Character Data";
165167localizedStrings["Charge ‘%s’ to Callers"] = "Charge ‘%s’ to Callers";
166168localizedStrings["Checked"] = "Checked";

@@localizedStrings["Continue script execution (%s or %s)"] = "Continue script exec
230232localizedStrings["Continue to Here"] = "Continue to Here";
231233localizedStrings["Controls"] = "Controls";
232234localizedStrings["Cookies"] = "Cookies";
 235localizedStrings["Copy Action"] = "Copy Action";
233236localizedStrings["Copy Link Address"] = "Copy Link Address";
234237localizedStrings["Copy Path to Property"] = "Copy Path to Property";
235238localizedStrings["Copy Row"] = "Copy Row";

@@localizedStrings["Exception with thrown value: %s"] = "Exception with thrown val
370373localizedStrings["Expand All"] = "Expand All";
371374localizedStrings["Expand columns"] = "Expand columns";
372375localizedStrings["Expanded"] = "Expanded";
 376localizedStrings["Experimental"] = "Experimental";
373377localizedStrings["Expires"] = "Expires";
 378localizedStrings["Export"] = "Export";
374379localizedStrings["Expression"] = "Expression";
375380localizedStrings["Extension Scripts"] = "Extension Scripts";
376381localizedStrings["Extra Scripts"] = "Extra Scripts";

@@localizedStrings["File or Resource"] = "File or Resource";
385390localizedStrings["Filename"] = "Filename";
386391localizedStrings["Fill"] = "Fill";
387392localizedStrings["Fill Mode"] = "Fill Mode";
 393localizedStrings["Filter Actions"] = "Filter Actions";
388394localizedStrings["Filter Console Log"] = "Filter Console Log";
389395localizedStrings["Filter List"] = "Filter List";
390396localizedStrings["Filter Records"] = "Filter Records";

@@localizedStrings["Heap Snapshot Object (%s)"] = "Heap Snapshot Object (%s)";
442448localizedStrings["Height"] = "Height";
443449localizedStrings["Hide Console"] = "Hide Console";
444450localizedStrings["Hide Grid"] = "Hide Grid";
 451localizedStrings["Hide Path"] = "Hide Path";
445452localizedStrings["Hide compositing borders"] = "Hide compositing borders";
446453localizedStrings["Hide shadow DOM nodes"] = "Hide shadow DOM nodes";
447454localizedStrings["Hide the details sidebar (%s)"] = "Hide the details sidebar (%s)";

@@localizedStrings["Image"] = "Image";
462469localizedStrings["Image Size"] = "Image Size";
463470localizedStrings["Images"] = "Images";
464471localizedStrings["Immediate Pause Requested"] = "Immediate Pause Requested";
 472localizedStrings["Import"] = "Import";
 473localizedStrings["Incomplete"] = "Incomplete";
465474localizedStrings["Indent"] = "Indent";
466475localizedStrings["Indent width:"] = "Indent width:";
467476localizedStrings["Index"] = "Index";

@@localizedStrings["Indexed Databases"] = "Indexed Databases";
470479localizedStrings["Info: "] = "Info: ";
471480localizedStrings["Inherited From: "] = "Inherited From: ";
472481localizedStrings["Inherited from %s"] = "Inherited from %s";
 482localizedStrings["Initial State"] = "Initial State";
473483localizedStrings["Initial Velocity"] = "Initial Velocity";
474484localizedStrings["Initiated"] = "Initiated";
475485localizedStrings["Initiator"] = "Initiator";

@@localizedStrings["No Preview Available"] = "No Preview Available";
579589localizedStrings["No Properties"] = "No Properties";
580590localizedStrings["No Properties \u2014 Click to Edit"] = "No Properties \u2014 Click to Edit";
581591localizedStrings["No Query Parameters"] = "No Query Parameters";
 592localizedStrings["No Recording Data"] = "No Recording Data";
582593localizedStrings["No Request Headers"] = "No Request Headers";
583594localizedStrings["No Response Headers"] = "No Response Headers";
584595localizedStrings["No Results Found"] = "No Results Found";
585596localizedStrings["No Search Results"] = "No Search Results";
 597localizedStrings["No Trace Data"] = "No Trace Data";
586598localizedStrings["No Watch Expressions"] = "No Watch Expressions";
587599localizedStrings["No matching ARIA role"] = "No matching ARIA role";
588600localizedStrings["No message"] = "No message";

@@localizedStrings["Range Issue"] = "Range Issue";
668680localizedStrings["Readonly"] = "Readonly";
669681localizedStrings["Reasons for compositing:"] = "Reasons for compositing:";
670682localizedStrings["Recently Closed Tabs"] = "Recently Closed Tabs";
 683localizedStrings["Recording"] = "Recording";
671684localizedStrings["Recording Timeline Data"] = "Recording Timeline Data";
 685localizedStrings["Recording error: %s"] = "Recording error: %s";
672686localizedStrings["Reference Issue"] = "Reference Issue";
673687localizedStrings["Reflection"] = "Reflection";
674688localizedStrings["Refresh"] = "Refresh";

@@localizedStrings["Refresh watch expressions"] = "Refresh watch expressions";
676690localizedStrings["Region Flow"] = "Region Flow";
677691localizedStrings["Region announced in its entirety."] = "Region announced in its entirety.";
678692localizedStrings["Regular Expression"] = "Regular Expression";
 693localizedStrings["Reload WebInspector"] = "Reload WebInspector";
679694localizedStrings["Reload this page (%s)\nReload ignoring cache (%s)"] = "Reload this page (%s)\nReload ignoring cache (%s)";
680695localizedStrings["Removals"] = "Removals";
681696localizedStrings["Remove Watch Expression"] = "Remove Watch Expression";

@@localizedStrings["Request"] = "Request";
692707localizedStrings["Request & Response"] = "Request & Response";
693708localizedStrings["Request Data"] = "Request Data";
694709localizedStrings["Request Headers"] = "Request Headers";
 710localizedStrings["Request recording of actions. Shift-click to record a single frame."] = "Request recording of actions. Shift-click to record a single frame.";
695711localizedStrings["Requesting: %s"] = "Requesting: %s";
696712localizedStrings["Required"] = "Required";
697713localizedStrings["Reset"] = "Reset";

@@localizedStrings["Show Console"] = "Show Console";
770786localizedStrings["Show Console tab"] = "Show Console tab";
771787localizedStrings["Show Contexts in Resources Tab"] = "Show Contexts in Resources Tab";
772788localizedStrings["Show Grid"] = "Show Grid";
 789localizedStrings["Show Path"] = "Show Path";
773790localizedStrings["Show Remaining (%d)"] = "Show Remaining (%d)";
774791localizedStrings["Show Scope Chain on pause"] = "Show Scope Chain on pause";
775792localizedStrings["Show Source"] = "Show Source";

@@localizedStrings["spaces"] = "spaces";
954971localizedStrings["time before stopping"] = "time before stopping";
955972localizedStrings["times before stopping"] = "times before stopping";
956973localizedStrings["toggle"] = "toggle";
 974localizedStrings["unsupported version"] = "unsupported version";
957975localizedStrings["value"] = "value";
958976localizedStrings["“%s” Profile Recorded"] = "“%s” Profile Recorded";
 977localizedStrings["“%s” is invalid"] = "“%s” is invalid";
 978localizedStrings["“%s” threw error"] = "“%s” threw error";

Source/WebInspectorUI/UserInterface/Base/Main.js

@@WebInspector.contentLoaded = function()
435435 WebInspector.ElementsTabContentView,
436436 WebInspector.NetworkTabContentView,
437437 WebInspector.NewTabContentView,
 438 WebInspector.RecordingTabContentView,
438439 WebInspector.ResourcesTabContentView,
439440 WebInspector.SearchTabContentView,
440441 WebInspector.SettingsTabContentView,

@@WebInspector.contentLoaded = function()
507508// This function returns a lazily constructed instance of a class scoped to this WebInspector
508509// instance. In the unlikely event that we ever need to construct multiple WebInspector instances
509510// this allows us to scope objects within the WebInspector.
 511// Classes can prevent usage of this function via a static `disallowInstanceForClass` function that
 512// returns true. It is then their responsibility to ensure that the returned value is tracked.
510513// Currently it is only used for sidebars.
511514WebInspector.instanceForClass = function(constructor)
512515{
513516 console.assert(typeof constructor === "function");
 517 if (typeof constructor.disallowInstanceForClass === "function" && constructor.disallowInstanceForClass())
 518 return new constructor;
514519
515520 let key = `__${constructor.name}`;
516521 if (!WebInspector[key])

@@WebInspector.saveDataToFile = function(saveData, forceSaveAs)
900905 });
901906};
902907
 908WebInspector.loadDataFromFile = function(callback)
 909{
 910 let inputElement = document.createElement("input");
 911 inputElement.type = "file";
 912 inputElement.addEventListener("change", (event) => {
 913 if (!inputElement.files.length)
 914 return;
 915
 916 let reader = new FileReader;
 917 reader.addEventListener("loadend", (event) => {
 918 callback(reader.result);
 919 });
 920 reader.readAsText(inputElement.files[0]);
 921 });
 922 inputElement.click();
 923};
 924
903925WebInspector.isConsoleFocused = function()
904926{
905927 return this.quickConsole.prompt.focused;

@@WebInspector.tabContentViewClassForRepresentedObject = function(representedObjec
11191141 representedObject instanceof WebInspector.IndexedDatabaseObjectStoreIndex)
11201142 return WebInspector.ResourcesTabContentView;
11211143
 1144 if (representedObject instanceof WebInspector.Recording)
 1145 return WebInspector.RecordingTabContentView;
 1146
11221147 return null;
11231148};
11241149

Source/WebInspectorUI/UserInterface/Base/Setting.js

@@WebInspector.settings = {
121121 stylesSelectOnFirstClick: new WebInspector.Setting("styles-select-on-first-click", true),
122122 showScopeChainOnPause: new WebInspector.Setting("show-scope-chain-sidebar", true),
123123 showImageGrid: new WebInspector.Setting("show-image-grid", false),
 124 showCanvasPath: new WebInspector.Setting("show-canvas-path", false),
124125
125126 // Experimental
126127 experimentalShowCanvasContextsInResources: new WebInspector.Setting("experimental-show-canvas-contexts-in-resources", false),

Source/WebInspectorUI/UserInterface/Base/Utilities.js

@@Object.defineProperty(Number, "abbreviate",
11191119 }
11201120});
11211121
 1122Object.defineProperty(Number, "countDigits",
 1123{
 1124 value(num)
 1125 {
 1126 return Math.floor(Math.log(num) * Math.LOG10E) + 1;
 1127 },
 1128});
 1129
11221130Object.defineProperty(Number.prototype, "maxDecimals",
11231131{
11241132 value(decimals)

Source/WebInspectorUI/UserInterface/Controllers/CanvasManager.js

@@WebInspector.CanvasManager = class CanvasManager extends WebInspector.Object
9696 canvas.cssCanvasClientNodesChanged();
9797 }
9898
 99 recordingFinished(canvasIdentifier, recordingPayload)
 100 {
 101 // Called from WebInspector.CanvasObserver.
 102
 103 let canvas = this._canvasIdentifierMap.get(canvasIdentifier);
 104 console.assert(canvas);
 105 if (!canvas)
 106 return;
 107
 108 let recording = WebInspector.Recording.fromPayload(recordingPayload);
 109 recording.source = canvas;
 110
 111 this.dispatchEventToListeners(WebInspector.CanvasManager.Event.RecordingFinished, {canvas, recording});
 112 }
 113
99114 // Private
100115
101116 _mainResourceDidChange(event)

@@WebInspector.CanvasManager.Event = {
117132 Cleared: "canvas-manager-cleared",
118133 CanvasWasAdded: "canvas-manager-canvas-was-added",
119134 CanvasWasRemoved: "canvas-manager-canvas-was-removed",
 135 RecordingFinished: "canvas-managger-recording-finished",
120136};

Source/WebInspectorUI/UserInterface/Images/Recording.svg

 1<?xml version="1.0" encoding="utf-8"?>
 2<!-- Copyright © 2017 Apple Inc. All rights reserved. -->
 3<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
 4 <path fill="currentColor" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/>
 5 <rect fill="currentColor" x="4.25" y="4.5" width="7.5" height="1"/>
 6 <rect fill="currentColor" x="4.25" y="6.5" width="7.5" height="1"/>
 7 <rect fill="currentColor" x="4.25" y="8.5" width="7.5" height="1"/>
 8 <rect fill="currentColor" x="4.25" y="10.5" width="7.5" height="1"/>
 9</svg>

Source/WebInspectorUI/UserInterface/Images/RenderingFrame.svg

was renamed from

Source/WebInspectorUI/UserInterface/Images/TimelineRecordRenderingFrame.svg

rename from Source/WebInspectorUI/UserInterface/Images/TimelineRecordRenderingFrame.svg
rename to Source/WebInspectorUI/UserInterface/Images/RenderingFrame.svg

Source/WebInspectorUI/UserInterface/Images/gtk/RenderingFrame.svg

was renamed from

Source/WebInspectorUI/UserInterface/Images/gtk/TimelineRecordRenderingFrame.svg

rename from Source/WebInspectorUI/UserInterface/Images/gtk/TimelineRecordRenderingFrame.svg
rename to Source/WebInspectorUI/UserInterface/Images/gtk/RenderingFrame.svg

Source/WebInspectorUI/UserInterface/Main.html

140140 <link rel="stylesheet" href="Views/ProfileView.css">
141141 <link rel="stylesheet" href="Views/QuickConsole.css">
142142 <link rel="stylesheet" href="Views/RadioButtonNavigationItem.css">
 143 <link rel="stylesheet" href="Views/RecordingContentView.css">
 144 <link rel="stylesheet" href="Views/RecordingStateDetailsSidebarPanel.css">
 145 <link rel="stylesheet" href="Views/RecordingTraceDetailsSidebarPanel.css">
 146 <link rel="stylesheet" href="Views/RecordingNavigationSidebarPanel.css">
143147 <link rel="stylesheet" href="Views/RenderingFrameTimelineOverviewGraph.css">
144148 <link rel="stylesheet" href="Views/RenderingFrameTimelineView.css">
145149 <link rel="stylesheet" href="Views/Resizer.css">

161165 <link rel="stylesheet" href="Views/Sidebar.css">
162166 <link rel="stylesheet" href="Views/SidebarPanel.css">
163167 <link rel="stylesheet" href="Views/Slider.css">
 168 <link rel="stylesheet" href="Views/ScrubberNavigationItem.css">
164169 <link rel="stylesheet" href="Views/SoftContextMenu.css">
165170 <link rel="stylesheet" href="Views/SourceCodeTextEditor.css">
166171 <link rel="stylesheet" href="Views/SpringEditor.css">

377382 <script src="Models/PropertyDescriptor.js"></script>
378383 <script src="Models/PropertyPath.js"></script>
379384 <script src="Models/PropertyPreview.js"></script>
 385 <script src="Models/Recording.js"></script>
 386 <script src="Models/RecordingAction.js"></script>
 387 <script src="Models/RecordingFrame.js"></script>
 388 <script src="Models/RecordingInitialStateAction.js"></script>
380389 <script src="Models/RenderingFrameTimelineRecord.js"></script>
381390 <script src="Models/Resource.js"></script>
382391 <script src="Models/ResourceCollection.js"></script>

473482 <script src="Views/ConsoleTabContentView.js"></script>
474483 <script src="Views/DebuggerTabContentView.js"></script>
475484 <script src="Views/ElementsTabContentView.js"></script>
 485 <script src="Views/RecordingTabContentView.js"></script>
476486 <script src="Views/ResourceTreeElement.js"></script>
477487 <script src="Views/ResourcesTabContentView.js"></script>
478488 <script src="Views/SearchTabContentView.js"></script>

639649 <script src="Views/QuickConsole.js"></script>
640650 <script src="Views/QuickConsoleNavigationBar.js"></script>
641651 <script src="Views/RadioButtonNavigationItem.js"></script>
 652 <script src="Views/RecordingActionTreeElement.js"></script>
 653 <script src="Views/RecordingContentView.js"></script>
 654 <script src="Views/RecordingNavigationSidebarPanel.js"></script>
 655 <script src="Views/RecordingStateDetailsSidebarPanel.js"></script>
 656 <script src="Views/RecordingTraceDetailsSidebarPanel.js"></script>
642657 <script src="Views/RenderingFrameTimelineDataGridNode.js"></script>
643658 <script src="Views/RenderingFrameTimelineOverviewGraph.js"></script>
644659 <script src="Views/RenderingFrameTimelineView.js"></script>

666681 <script src="Views/SearchSidebarPanel.js"></script>
667682 <script src="Views/Sidebar.js"></script>
668683 <script src="Views/Slider.js"></script>
 684 <script src="Views/ScrubberNavigationItem.js"></script>
669685 <script src="Views/SoftContextMenu.js"></script>
670686 <script src="Views/SourceCodeTextEditor.js"></script>
671687 <script src="Views/SourceCodeTimelineTimelineDataGridNode.js"></script>

Source/WebInspectorUI/UserInterface/Models/Canvas.js

@@WebInspector.Canvas = class Canvas extends WebInspector.Object
194194 });
195195 }
196196
 197 toggleRecording(flag, singleFrame, callback)
 198 {
 199 if (flag)
 200 CanvasAgent.requestRecording(this._identifier, singleFrame, callback);
 201 else
 202 CanvasAgent.cancelRecording(this._identifier, callback);
 203 }
 204
197205 saveIdentityToCookie(cookie)
198206 {
199207 cookie[WebInspector.Canvas.FrameURLCookieKey] = this._frame.url.hash;

Source/WebInspectorUI/UserInterface/Models/Recording.js

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.Recording = class Recording
 27{
 28 constructor(version, type, initialState, frames, data)
 29 {
 30 this._version = version;
 31 this._type = type;
 32 this._initialState = initialState;
 33 this._frames = frames;
 34 this._data = data;
 35
 36 this._actions = [new WebInspector.RecordingInitialStateAction].concat(...this._frames.map((frame) => frame.actions));
 37 this._swizzle = [];
 38 this._source = null;
 39
 40 for (let frame of this._frames) {
 41 for (let action of frame.actions) {
 42 action.swizzle(this);
 43
 44 let prototype = null;
 45 if (this._type === WebInspector.Recording.Type.Canvas2D)
 46 prototype = CanvasRenderingContext2D.prototype;
 47
 48 if (prototype) {
 49 let validName = action.name in prototype;
 50 let validFunction = !action.functionForType(this._type) || typeof prototype[action.name] === "function";
 51 if (!validName || !validFunction) {
 52 action.valid = false;
 53
 54 WebInspector.Recording.synthesizeError(WebInspector.UIString("“%s” is invalid").format(action.name));
 55 }
 56 }
 57 }
 58 }
 59 }
 60
 61 // Static
 62
 63 static fromPayload(payload)
 64 {
 65 if (typeof payload !== "object" || payload === null)
 66 payload = {};
 67
 68 if (isNaN(payload.version) || payload.version <= 0)
 69 return null;
 70
 71 if (typeof payload.initialState !== "object" || payload.initialState === null)
 72 payload.initialState = {};
 73 if (typeof payload.initialState.attributes !== "object" || payload.initialState.attributes === null)
 74 payload.initialState.attributes = {};
 75 if (!Array.isArray(payload.initialState.parameters))
 76 payload.initialState.parameters = [];
 77 if (typeof payload.initialState.content !== "string")
 78 payload.initialState.content = "";
 79
 80 if (!Array.isArray(payload.frames))
 81 payload.frames = [];
 82
 83 if (!Array.isArray(payload.data))
 84 payload.data = [];
 85
 86 let frames = payload.frames.map(WebInspector.RecordingFrame.fromPayload);
 87 return new WebInspector.Recording(payload.version, payload.type, payload.initialState, frames, payload.data);
 88 }
 89
 90 static synthesizeError(message)
 91 {
 92 const target = WebInspector.mainTarget;
 93 const source = WebInspector.ConsoleMessage.MessageSource.Other;
 94 const level = WebInspector.ConsoleMessage.MessageLevel.Error;
 95 let consoleMessage = new WebInspector.ConsoleMessage(target, source, level, WebInspector.UIString("Recording error: %s").format(message));
 96 consoleMessage.shouldRevealConsole = true; // TODO
 97
 98 WebInspector.consoleLogViewController.appendConsoleMessage(consoleMessage);
 99 }
 100
 101 // Public
 102
 103 get type() { return this._type; }
 104 get initialState() { return this._initialState; }
 105 get frames() { return this._frames; }
 106 get data() { return this._data; }
 107
 108 get actions() { return this._actions; }
 109
 110 get source() { return this._source; }
 111 set source(source) { this._source = source; }
 112
 113 swizzle(index, type)
 114 {
 115 if (typeof this._swizzle[index] !== "object")
 116 this._swizzle[index] = {};
 117
 118 if (!(type in this._swizzle[index])) {
 119 try {
 120 let data = this._data[index];
 121 switch (type) {
 122 case WebInspector.Recording.Swizzle.Array:
 123 if (Array.isArray(data))
 124 this._swizzle[index][type] = data;
 125 break;
 126 case WebInspector.Recording.Swizzle.CanvasStyle:
 127 if (Array.isArray(data)) {
 128 let context = document.createElement("canvas").getContext("2d");
 129
 130 let canvasStyle = this.swizzle(data[0], WebInspector.Recording.Swizzle.String);
 131 if (canvasStyle === "linear-gradient" || canvasStyle === "radial-gradient") {
 132 this._swizzle[index][type] = canvasStyle === "radial-gradient" ? context.createRadialGradient(...data[1]) : context.createLinearGradient(...data[1]);
 133 for (let stop of data[2]) {
 134 let color = this.swizzle(stop[1], WebInspector.Recording.Swizzle.String);
 135 this._swizzle[index][type].addColorStop(stop[0], color);
 136 }
 137 } else if (canvasStyle === "pattern") {
 138 let image = this.swizzle(data[1], WebInspector.Recording.Swizzle.Image);
 139 let repeat = this.swizzle(data[2], WebInspector.Recording.Swizzle.String);
 140 this._swizzle[index][type] = context.createPattern(image, repeat);
 141 }
 142 } else if (typeof data === "string")
 143 this._swizzle[index][type] = data;
 144 break;
 145 case WebInspector.Recording.Swizzle.Element:
 146 this._swizzle[index][type] = WebInspector.Recording.Swizzle.Invalid;
 147 break;
 148 case WebInspector.Recording.Swizzle.Image:
 149 this._swizzle[index][type] = new Image;
 150 this._swizzle[index][type].src = data;
 151 break;
 152 case WebInspector.Recording.Swizzle.ImageData:
 153 this._swizzle[index][type] = new ImageData(new Uint8ClampedArray(data[0]), parseInt(data[1]), parseInt(data[2]));
 154 break;
 155 case WebInspector.Recording.Swizzle.Path2D:
 156 this._swizzle[index][type] = new Path2D(data);
 157 break;
 158 case WebInspector.Recording.Swizzle.String:
 159 if (typeof data === "string")
 160 this._swizzle[index][type] = data;
 161 break;
 162 }
 163 } catch (e) {
 164 this._swizzle[index][type] = WebInspector.Recording.Swizzle.Invalid;
 165 }
 166
 167 if (!(type in this._swizzle[index]))
 168 this._swizzle[index][type] = WebInspector.Recording.Swizzle.Invalid;
 169 }
 170
 171 return this._swizzle[index][type];
 172 }
 173
 174 toJSON()
 175 {
 176 let initialState = {};
 177 if (!isEmptyObject(this._initialState.attributes))
 178 initialState.attributes = this._initialState.attributes;
 179 if (this._initialState.parameters.length)
 180 initialState.parameters = this._initialState.parameters;
 181 if (this._initialState.content && this._initialState.content.length)
 182 initialState.content = this._initialState.content;
 183
 184 return {
 185 version: this._version,
 186 type: this._type,
 187 initialState,
 188 frames: this._frames.map((frame) => frame.toJSON()),
 189 data: this._data,
 190 };
 191 }
 192};
 193
 194WebInspector.Recording.Version = 1;
 195
 196WebInspector.Recording.Type = {
 197 Canvas2D: CanvasAgent.ContextType.Canvas2D,
 198};
 199
 200WebInspector.Recording.Swizzle = {
 201 Array: "Array",
 202 CanvasStyle: "CanvasStyle",
 203 Element: "Element",
 204 Image: "Image",
 205 ImageData: "ImageData",
 206 Path2D: "Path2D",
 207 String: "String",
 208 Invalid: Symbol("invalid"),
 209};

Source/WebInspectorUI/UserInterface/Models/RecordingAction.js

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.RecordingAction = class RecordingAction
 27{
 28 constructor(name, parameters, trace)
 29 {
 30 this._name = name;
 31 this._parameters = parameters;
 32 this._trace = trace;
 33
 34 this._resolvedName = "";
 35 this._resolvedParameters = [];
 36 this._resolvedTrace = [];
 37
 38 this._valid = true;
 39 }
 40
 41 // Static
 42
 43 static fromPayload(payload)
 44 {
 45 if (!Array.isArray(payload))
 46 payload = [];
 47
 48 if (isNaN(payload[0])) // Name
 49 payload[0] = -1;
 50
 51 if (!Array.isArray(payload[1])) // Parameters
 52 payload[1] = [];
 53
 54 if (!Array.isArray(payload[2])) // Trace
 55 payload[2] = [];
 56
 57 return new WebInspector.RecordingAction(...payload);
 58 }
 59
 60 static functionForType(type, name)
 61 {
 62 let forType = WebInspector.RecordingAction._functionNames[type];
 63 if (!forType)
 64 return false;
 65
 66 return forType.includes(name);
 67 }
 68
 69 // Public
 70
 71 get name() { return this._resolvedName; }
 72 get parameters() { return this._resolvedParameters; }
 73 get trace() { return this._resolvedTrace; }
 74
 75 get valid() { return this._valid; }
 76 set valid(valid) { this._valid = !!valid; }
 77
 78 swizzle(recording)
 79 {
 80 this._resolvedName = recording.swizzle(this._name, WebInspector.Recording.Swizzle.String);
 81
 82 this._resolvedParameters = this._parameters.map((item, i) => {
 83 let type = this.parameterSwizzleTypeForTypeAtIndex(recording.type, i);
 84 if (!type)
 85 return item;
 86
 87 return recording.swizzle(item, type);
 88 });
 89
 90 for (let item of this._trace) {
 91 try {
 92 let array = recording.swizzle(item, WebInspector.Recording.Swizzle.Array);
 93 let callFrame = WebInspector.CallFrame.fromPayload(WebInspector.mainTarget, {
 94 functionName: recording.swizzle(array[0], WebInspector.Recording.Swizzle.String),
 95 url: recording.swizzle(array[1], WebInspector.Recording.Swizzle.String),
 96 lineNumber: array[2],
 97 columnNumber: array[3],
 98 });
 99 this._resolvedTrace.push(callFrame);
 100 } catch (e) {
 101 // Ignored.
 102 }
 103 }
 104 }
 105
 106 parameterSwizzleTypeForTypeAtIndex(type, index)
 107 {
 108 let forType = WebInspector.RecordingAction._parameterSwizzleTypeForTypeAtIndex[type];
 109 if (!forType)
 110 return null;
 111
 112 let forName = forType[this._resolvedName];
 113 if (!forName)
 114 return null;
 115
 116 let forArgumentsLength = forName[this._parameters.length];
 117 if (!forArgumentsLength)
 118 return null;
 119
 120 return forArgumentsLength[index] || null;
 121 }
 122
 123 functionForType(type)
 124 {
 125 return WebInspector.RecordingAction.functionForType(type, this._resolvedName);
 126 }
 127
 128 getterForType(type)
 129 {
 130 return !this.functionForType(type) && !this._resolvedParameters.length;
 131 }
 132
 133 visualForType(type)
 134 {
 135 let forType = WebInspector.RecordingAction._visualNames[type];
 136 if (!forType)
 137 return false;
 138
 139 return forType.includes(this._resolvedName);
 140 }
 141
 142 initialStateModifiersForType(type)
 143 {
 144 let initialStateModifiers = [this._resolvedName];
 145
 146 let forType = WebInspector.RecordingAction._initialStateModifiers[type];
 147 if (forType) {
 148 let forName = forType[this._resolvedName];
 149 if (forName)
 150 initialStateModifiers = initialStateModifiers.concat(forName);
 151 }
 152
 153 return initialStateModifiers;
 154 }
 155
 156 toJSON()
 157 {
 158 return [this._name, this._parameters, this._trace];
 159 }
 160};
 161
 162WebInspector.RecordingAction._parameterSwizzleTypeForTypeAtIndex = {
 163 [WebInspector.Recording.Type.Canvas2D]: {
 164 "clip": {
 165 1: {0: WebInspector.Recording.Swizzle.String},
 166 2: {0: WebInspector.Recording.Swizzle.Path2D, 1: WebInspector.Recording.Swizzle.String},
 167 },
 168 "createImageData": {
 169 1: {0: WebInspector.Recording.Swizzle.ImageData},
 170 },
 171 "createPattern": {
 172 2: {0: WebInspector.Recording.Swizzle.Image, 1: WebInspector.Recording.Swizzle.String},
 173 },
 174 "direction": {
 175 1: {0: WebInspector.Recording.Swizzle.String},
 176 },
 177 "drawImage": {
 178 3: {0: WebInspector.Recording.Swizzle.Image},
 179 5: {0: WebInspector.Recording.Swizzle.Image},
 180 9: {0: WebInspector.Recording.Swizzle.Image},
 181 },
 182 "drawImageFromRect": {
 183 10: {0: WebInspector.Recording.Swizzle.Image, 9: WebInspector.Recording.Swizzle.String},
 184 },
 185 "drawFocusIfNeeded": {
 186 1: {0: WebInspector.Recording.Swizzle.Element},
 187 2: {0: WebInspector.Recording.Swizzle.Path2D, 1: WebInspector.Recording.Swizzle.Element},
 188 },
 189 "fill": {
 190 1: {0: WebInspector.Recording.Swizzle.String},
 191 2: {0: WebInspector.Recording.Swizzle.Path2D, 1: WebInspector.Recording.Swizzle.String},
 192 },
 193 "fillStyle": {
 194 1: {0: WebInspector.Recording.Swizzle.CanvasStyle},
 195 },
 196 "fillText": {
 197 3: {0: WebInspector.Recording.Swizzle.String},
 198 4: {0: WebInspector.Recording.Swizzle.String},
 199 },
 200 "font": {
 201 1: {0: WebInspector.Recording.Swizzle.String},
 202 },
 203 "globalCompositeOperation": {
 204 1: {0: WebInspector.Recording.Swizzle.String},
 205 },
 206 "imageSmoothingQuality": {
 207 1: {0: WebInspector.Recording.Swizzle.String},
 208 },
 209 "isPointInPath": {
 210 4: {0: WebInspector.Recording.Swizzle.Path2D, 3: WebInspector.Recording.Swizzle.String},
 211 },
 212 "isPointInStroke": {
 213 3: {0: WebInspector.Recording.Swizzle.Path2D, 3: WebInspector.Recording.Swizzle.String},
 214 },
 215 "lineCap": {
 216 1: {0: WebInspector.Recording.Swizzle.String},
 217 },
 218 "lineJoin": {
 219 1: {0: WebInspector.Recording.Swizzle.String},
 220 },
 221 "measureText": {
 222 1: {0: WebInspector.Recording.Swizzle.String},
 223 },
 224 "putImageData": {
 225 3: {0: WebInspector.Recording.Swizzle.ImageData},
 226 7: {0: WebInspector.Recording.Swizzle.ImageData},
 227 },
 228 "setCompositeOperation": {
 229 1: {0: WebInspector.Recording.Swizzle.String},
 230 },
 231 "setFillColor": {
 232 1: {0: WebInspector.Recording.Swizzle.String},
 233 2: {0: WebInspector.Recording.Swizzle.String},
 234 },
 235 "setLineCap": {
 236 1: {0: WebInspector.Recording.Swizzle.String},
 237 },
 238 "setLineJoin": {
 239 1: {0: WebInspector.Recording.Swizzle.String},
 240 },
 241 "setShadow": {
 242 4: {3: WebInspector.Recording.Swizzle.String},
 243 5: {3: WebInspector.Recording.Swizzle.String},
 244 },
 245 "setStrokeColor": {
 246 1: {0: WebInspector.Recording.Swizzle.String},
 247 2: {0: WebInspector.Recording.Swizzle.String},
 248 },
 249 "shadowColor": {
 250 1: {0: WebInspector.Recording.Swizzle.String},
 251 },
 252 "stroke": {
 253 1: {0: WebInspector.Recording.Swizzle.Path2D},
 254 },
 255 "strokeStyle": {
 256 1: {0: WebInspector.Recording.Swizzle.CanvasStyle},
 257 },
 258 "strokeText": {
 259 3: {0: WebInspector.Recording.Swizzle.String},
 260 4: {0: WebInspector.Recording.Swizzle.String},
 261 },
 262 "textAlign": {
 263 1: {0: WebInspector.Recording.Swizzle.String},
 264 },
 265 "textBaseline": {
 266 1: {0: WebInspector.Recording.Swizzle.String},
 267 },
 268 "webkitPutImageData": {
 269 3: {0: WebInspector.Recording.Swizzle.ImageData},
 270 7: {0: WebInspector.Recording.Swizzle.ImageData},
 271 },
 272 },
 273};
 274
 275WebInspector.RecordingAction._functionNames = {
 276 [WebInspector.Recording.Type.Canvas2D]: ["arc", "arcTo", "beginPath", "bezierCurveTo", "clearRect", "clearShadow", "clip", "clip", "closePath", "commit", "createImageData", "createLinearGradient", "createPattern", "createRadialGradient", "drawFocusIfNeeded", "drawImage", "drawImageFromRect", "ellipse", "fill", "fill", "fillRect", "fillText", "getImageData", "getLineDash", "isPointInPath", "isPointInPath", "isPointInStroke", "isPointInStroke", "lineTo", "measureText", "moveTo", "putImageData", "quadraticCurveTo", "rect", "resetTransform", "restore", "rotate", "save", "scale", "setAlpha", "setCompositeOperation", "setFillColor", "setLineCap", "setLineDash", "setLineJoin", "setLineWidth", "setMiterLimit", "setShadow", "setStrokeColor", "setTransform", "stroke", "stroke", "strokeRect", "strokeText", "transform", "translate", "webkitGetImageDataHD", "webkitPutImageDataHD"],
 277};
 278
 279WebInspector.RecordingAction._visualNames = {
 280 [WebInspector.Recording.Type.Canvas2D]: ["clearRect", "drawFocusIfNeeded", "drawImage", "drawImageFromRect", "fill", "fillRect", "fillText", "putImageData", "stroke", "strokeRect", "strokeText", "webkitPutImageDataHD"],
 281};
 282
 283WebInspector.RecordingAction._initialStateModifiers = {
 284 [WebInspector.Recording.Type.Canvas2D]: {
 285 clearShadow: ["shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor"],
 286 resetTransform: ["transform"],
 287 rotate: ["transform"],
 288 scale: ["transform"],
 289 setAlpha: ["globalAlpha"],
 290 setCompositeOperation: ["globalCompositeOperation"],
 291 setFillColor: ["fillStyle"],
 292 setLineCap: ["lineCap"],
 293 setLineJoin: ["lineJoin"],
 294 setLineWidth: ["lineWidth"],
 295 setMiterLimit: ["miterLimit"],
 296 setShadow: ["shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor"],
 297 setStrokeColor: ["strokeStyle"],
 298 setTransform: ["transform"],
 299 translate: ["transform"],
 300 },
 301};

Source/WebInspectorUI/UserInterface/Models/RecordingFrame.js

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.RecordingFrame = class RecordingFrame
 27{
 28 constructor(actions, {incomplete} = {})
 29 {
 30 this._actions = actions;
 31 this._incomplete = incomplete;
 32 }
 33
 34 // Static
 35
 36 static fromPayload(payload)
 37 {
 38 if (typeof payload !== "object" || payload === null)
 39 payload = {};
 40
 41 if (!Array.isArray(payload.actions))
 42 payload.actions = [];
 43
 44 let actions = payload.actions.map(WebInspector.RecordingAction.fromPayload);
 45 return new WebInspector.RecordingFrame(actions, {
 46 incomplete: !!payload.incomplete,
 47 });
 48 }
 49
 50 // Public
 51
 52 get actions() { return this._actions; }
 53 get incomplete() { return this._incomplete; }
 54
 55 toJSON()
 56 {
 57 let json = {
 58 actions: this._actions.map((action) => action.toJSON()),
 59 };
 60 if (this._incomplete)
 61 json.incomplete = this._incomplete;
 62 return json;
 63 }
 64};

Source/WebInspectorUI/UserInterface/Models/RecordingInitialStateAction.js

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.RecordingInitialStateAction = class RecordingInitialStateAction extends WebInspector.RecordingAction
 27{
 28 constructor()
 29 {
 30 super();
 31
 32 this._resolvedName = WebInspector.UIString("Initial State");
 33
 34 this._valid = false;
 35 }
 36};

Source/WebInspectorUI/UserInterface/Protocol/CanvasObserver.js

@@WebInspector.CanvasObserver = class CanvasObserver
4646 {
4747 WebInspector.canvasManager.cssCanvasClientNodesChanged(canvasId);
4848 }
 49
 50 recordingFinished(canvasId, recording)
 51 {
 52 WebInspector.canvasManager.recordingFinished(canvasId, recording);
 53 }
4954};

Source/WebInspectorUI/UserInterface/Test.html

151151 <script src="Models/ProfileNodeCall.js"></script>
152152 <script src="Models/PropertyDescriptor.js"></script>
153153 <script src="Models/PropertyPreview.js"></script>
 154 <script src="Models/Recording.js"></script>
 155 <script src="Models/RecordingAction.js"></script>
 156 <script src="Models/RecordingFrame.js"></script>
 157 <script src="Models/RecordingInitialStateAction.js"></script>
154158 <script src="Models/RenderingFrameTimelineRecord.js"></script>
155159 <script src="Models/Resource.js"></script>
156160 <script src="Models/ResourceCollection.js"></script>

Source/WebInspectorUI/UserInterface/Views/ButtonNavigationItem.js

@@WebInspector.ButtonNavigationItem = class ButtonNavigationItem extends WebInspec
128128 {
129129 if (!this.enabled)
130130 return;
131  this.dispatchEventToListeners(WebInspector.ButtonNavigationItem.Event.Clicked);
 131 this.dispatchEventToListeners(WebInspector.ButtonNavigationItem.Event.Clicked, {nativeEvent: event});
132132 }
133133};
134134

Source/WebInspectorUI/UserInterface/Views/CallFrameView.css

2424 */
2525
2626.call-frame {
 27 overflow: hidden;
 28 text-overflow: ellipsis;
2729 white-space: nowrap;
2830 cursor: pointer;
 31
 32 --icon-margin-end: 3px;
2933}
3034
3135.call-frame .icon {

3539 height: 16px;
3640}
3741
 42body[dir=ltr] .call-frame .icon {
 43 margin-right: var(--icon-margin-end);
 44}
 45
 46body[dir=rtl] .call-frame .icon {
 47 margin-left: var(--icon-margin-end);
 48}
 49
3850.call-frame .titles {
3951 display: inline-block;
4052}

Source/WebInspectorUI/UserInterface/Views/CanvasContentView.css

2323 * THE POSSIBILITY OF SUCH DAMAGE.
2424 */
2525
 26.content-view.canvas {
 27 background-color: hsl(0, 0%, 90%);
 28}
 29
2630.content-view.canvas > .preview {
2731 display: flex;
2832 justify-content: center;

Source/WebInspectorUI/UserInterface/Views/CanvasContentView.js

@@WebInspector.CanvasContentView = class CanvasContentView extends WebInspector.Co
3737 this._previewImageElement = null;
3838 this._errorElement = null;
3939
 40 if (representedObject.contextType === WebInspector.Canvas.ContextType.Canvas2D) {
 41 const toolTip = WebInspector.UIString("Request recording of actions. Shift-click to record a single frame.");
 42 const altToolTip = WebInspector.UIString("Cancel recording");
 43 this._recordButtonNavigationItem = new WebInspector.ToggleButtonNavigationItem("canvas-record", toolTip, altToolTip, "Images/Record.svg", "Images/Stop.svg", 13, 13);
 44 this._recordButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._toggleRecording, this);
 45 }
 46
4047 this._refreshButtonNavigationItem = new WebInspector.ButtonNavigationItem("canvas-refresh", WebInspector.UIString("Refresh"), "Images/ReloadFull.svg", 13, 13);
4148 this._refreshButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._showPreview, this);
4249

@@WebInspector.CanvasContentView = class CanvasContentView extends WebInspector.Co
4956
5057 get navigationItems()
5158 {
52  return [this._refreshButtonNavigationItem, this._showGridButtonNavigationItem];
 59 let navigationItems = [this._refreshButtonNavigationItem, this._showGridButtonNavigationItem];
 60 if (this._recordButtonNavigationItem)
 61 navigationItems.unshift(this._recordButtonNavigationItem);
 62 return navigationItems;
 63 }
 64
 65 initialLayout()
 66 {
 67 super.initialLayout();
 68
 69 WebInspector.canvasManager.addEventListener(WebInspector.CanvasManager.Event.RecordingFinished, this._recordingFinished, this);
5370 }
5471
5572 shown()

@@WebInspector.CanvasContentView = class CanvasContentView extends WebInspector.Co
6885 super.hidden();
6986 }
7087
 88 closed()
 89 {
 90 WebInspector.canvasManager.removeEventListener(null, null, this);
 91
 92 super.closed();
 93 }
 94
7195 // Private
7296
 97 _toggleRecording(event)
 98 {
 99 let toggled = this._recordButtonNavigationItem.toggled;
 100 let singleFrame = event.data.nativeEvent.shiftKey;
 101 this.representedObject.toggleRecording(!toggled, singleFrame, (error) => {
 102 if (error)
 103 return;
 104
 105 this._recordButtonNavigationItem.toggled = !toggled;
 106 });
 107 }
 108
 109 _recordingFinished(event)
 110 {
 111 if (event.data.canvas !== this.representedObject)
 112 return;
 113
 114 if (this._recordButtonNavigationItem)
 115 this._recordButtonNavigationItem.toggled = false;
 116
 117 WebInspector.showRepresentedObject(event.data.recording);
 118 }
 119
73120 _showPreview()
74121 {
75122 let showError = () => {

Source/WebInspectorUI/UserInterface/Views/CanvasDetailsSidebarPanel.js

@@WebInspector.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends
4242 if (!(objects instanceof Array))
4343 objects = [objects];
4444
 45 objects = objects.map((object) => object instanceof WebInspector.Recording ? object.source : object);
 46
4547 this.canvas = objects.find((object) => object instanceof WebInspector.Canvas);
4648
4749 return !!this._canvas;

Source/WebInspectorUI/UserInterface/Views/ContentBrowser.js

2525
2626WebInspector.ContentBrowser = class ContentBrowser extends WebInspector.View
2727{
28  constructor(element, delegate, disableBackForward, disableFindBanner)
 28 constructor(element, delegate, disableBackForward, disableFindBanner, flexibleNavigationItem)
2929 {
3030 super(element);
3131

@@WebInspector.ContentBrowser = class ContentBrowser extends WebInspector.View
7979
8080 this._contentViewSelectionPathNavigationItem = new WebInspector.HierarchicalPathNavigationItem;
8181
82  this._dividingFlexibleSpaceNavigationItem = new WebInspector.FlexibleSpaceNavigationItem;
83  this._navigationBar.addNavigationItem(this._dividingFlexibleSpaceNavigationItem);
 82 this._flexibleNavigationItem = flexibleNavigationItem || new WebInspector.FlexibleSpaceNavigationItem;
 83 this._navigationBar.addNavigationItem(this._flexibleNavigationItem);
8484
8585 WebInspector.ContentView.addEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._contentViewSelectionPathComponentDidChange, this);
8686 WebInspector.ContentView.addEventListener(WebInspector.ContentView.Event.SupplementalRepresentedObjectsDidChange, this._contentViewSupplementalRepresentedObjectsDidChange, this);

@@WebInspector.ContentBrowser = class ContentBrowser extends WebInspector.View
392392 this._removeAllNavigationItems();
393393
394394 let navigationBar = this.navigationBar;
395  let insertionIndex = navigationBar.navigationItems.indexOf(this._dividingFlexibleSpaceNavigationItem) + 1;
 395 let insertionIndex = navigationBar.navigationItems.indexOf(this._flexibleNavigationItem) + 1;
396396 console.assert(insertionIndex >= 0);
397397
398398 // Keep track of items we'll be adding to the navigation bar.

Source/WebInspectorUI/UserInterface/Views/ContentBrowserTabContentView.js

2525
2626WebInspector.ContentBrowserTabContentView = class ContentBrowserTabContentView extends WebInspector.TabContentView
2727{
28  constructor(identifier, styleClassNames, tabBarItem, navigationSidebarPanelConstructor, detailsSidebarPanelConstructors, disableBackForward)
 28 constructor(identifier, styleClassNames, tabBarItem, navigationSidebarPanelConstructor, detailsSidebarPanelConstructors, disableBackForward, flexibleNavigationItem)
2929 {
3030 if (typeof styleClassNames === "string")
3131 styleClassNames = [styleClassNames];
3232
3333 styleClassNames.push("content-browser");
3434
35  var contentBrowser = new WebInspector.ContentBrowser(null, null, disableBackForward);
 35 var contentBrowser = new WebInspector.ContentBrowser(null, null, disableBackForward, false, flexibleNavigationItem);
3636
3737 super(identifier, styleClassNames, tabBarItem, navigationSidebarPanelConstructor, detailsSidebarPanelConstructors);
3838

Source/WebInspectorUI/UserInterface/Views/ContentView.js

@@WebInspector.ContentView = class ContentView extends WebInspector.View
158158 if (representedObject instanceof WebInspector.HeapSnapshotProxy || representedObject instanceof WebInspector.HeapSnapshotDiffProxy)
159159 return new WebInspector.HeapSnapshotClusterContentView(representedObject, extraArguments);
160160
 161 if (representedObject instanceof WebInspector.Recording)
 162 return new WebInspector.RecordingContentView(representedObject, extraArguments);
 163
161164 if (representedObject instanceof WebInspector.Collection)
162165 return new WebInspector.CollectionContentView(representedObject, extraArguments);
163166

@@WebInspector.ContentView = class ContentView extends WebInspector.View
280283 return true;
281284 if (representedObject instanceof WebInspector.HeapSnapshotProxy || representedObject instanceof WebInspector.HeapSnapshotDiffProxy)
282285 return true;
 286 if (representedObject instanceof WebInspector.Recording)
 287 return true;
283288 if (representedObject instanceof WebInspector.Collection)
284289 return true;
285290 if (typeof representedObject === "string" || representedObject instanceof String)

Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.css

@@body[dir=rtl] .tree-outline.dom li.parent.shadow::after {
289289}
290290
291291@keyframes node-state-changed {
292  from { background-color: hsla(83, 100%, 48%, 0.4); }
 292 from { background-color: var(--value-changed-highlight); }
293293}
294294
295295.node-state-changed {

Source/WebInspectorUI/UserInterface/Views/DataGridNode.js

2525
2626WebInspector.DataGridNode = class DataGridNode extends WebInspector.Object
2727{
28  constructor(data, hasChildren)
 28 constructor(data, hasChildren, classNames)
2929 {
3030 super();
3131

@@WebInspector.DataGridNode = class DataGridNode extends WebInspector.Object
4242 this.previousSibling = null;
4343 this.nextSibling = null;
4444 this.disclosureToggleWidth = 10;
 45 this._classNames = classNames || [];
4546 }
4647
4748 get hidden()

@@WebInspector.DataGridNode = class DataGridNode extends WebInspector.Object
100101 this._element.classList.add("revealed");
101102 if (this._hidden)
102103 this._element.classList.add("hidden");
 104 this._element.classList.add(...this._classNames);
103105
104106 this.createCells();
105107 return this._element;

Source/WebInspectorUI/UserInterface/Views/Main.css

@@body[dir=rtl] .go-to-arrow {
368368 }
369369}
370370
371 img.show-grid {
 371:matches(img, canvas).show-grid {
372372 background-image: linear-gradient(315deg, transparent 75%, hsl(0, 0%, 95%) 75%),
373373 linear-gradient(45deg, transparent 75%, hsl(0, 0%, 95%) 75%),
374374 linear-gradient(315deg, hsl(0, 0%, 95%) 25%, transparent 25%),

Source/WebInspectorUI/UserInterface/Views/NavigationBar.css

2626.navigation-bar {
2727 display: flex;
2828 justify-content: center;
 29 align-items: center;
2930 flex-wrap: wrap;
3031
3132 border-bottom: 1px solid var(--border-color);

Source/WebInspectorUI/UserInterface/Views/NetworkSidebarPanel.css

9292}
9393
9494.sidebar > .panel.navigation.network.network-grid-content-view-showing > .content > .tree-outline {
95  background-image: linear-gradient(to bottom, transparent, transparent 50%, hsla(0, 0%, 0%, 0.03) 50%, hsla(0, 0%, 0%, 0.03));
96  background-size: 100% 40px;
 95 background: var(--transparent-stripe-background-gradient);
9796}
9897
9998.sidebar > .panel.navigation.network .tree-outline > .preserved:not(.selected) > :not(.status) {

Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.js

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.RecordingActionTreeElement = class RecordingActionTreeElement extends WebInspector.GeneralTreeElement
 27{
 28 constructor(representedObject, index, recordingType)
 29 {
 30 console.assert(representedObject instanceof WebInspector.RecordingAction);
 31
 32 let classNames = ["action"];
 33
 34 let isInitialState = representedObject instanceof WebInspector.RecordingInitialStateAction;
 35 let isGetter = representedObject.getterForType(recordingType);
 36 let isFunction = representedObject.functionForType(recordingType);
 37 if (isFunction)
 38 classNames.push("function");
 39 else if (!isInitialState) {
 40 classNames.push("attribute");
 41 if (isGetter)
 42 classNames.push("getter");
 43 else
 44 classNames.push(typeof representedObject.parameters[0]);
 45 }
 46
 47 if (representedObject.visualForType(recordingType))
 48 classNames.push("visual");
 49
 50 if (!representedObject.valid)
 51 classNames.push("invalid");
 52
 53 let titleFragment = document.createDocumentFragment();
 54
 55 let nameContainer = titleFragment.appendChild(document.createElement("span"));
 56 nameContainer.classList.add("name");
 57 nameContainer.textContent = representedObject.name;
 58
 59 if (!isGetter && !isInitialState) {
 60 let hasMissingParameter = false;
 61
 62 titleFragment.append(isFunction ? "(" : " = ");
 63
 64 for (let i = 0; i < representedObject.parameters.length; ++i) {
 65 if (i)
 66 titleFragment.append(", ");
 67
 68 let parameterElement = titleFragment.appendChild(document.createElement("span"));
 69 parameterElement.classList.add("parameter");
 70
 71 let swizzleType = representedObject.parameterSwizzleTypeForTypeAtIndex(recordingType, i);
 72 if (swizzleType && typeof representedObject.parameters[i] !== "string") {
 73 parameterElement.classList.add("swizzled");
 74 if (representedObject.parameters[i] === WebInspector.Recording.Swizzle.Invalid) {
 75 parameterElement.classList.add("missing");
 76
 77 hasMissingParameter = true;
 78 }
 79
 80 if (representedObject.parameters[i] instanceof CanvasGradient)
 81 parameterElement.textContent = WebInspector.unlocalizedString("CanvasGradient");
 82 else if (representedObject.parameters[i] instanceof CanvasPattern)
 83 parameterElement.textContent = WebInspector.unlocalizedString("CanvasPattern");
 84 else
 85 parameterElement.textContent = swizzleType;
 86 } else
 87 parameterElement.textContent = JSON.stringify(representedObject.parameters[i]);
 88
 89 if (!isFunction)
 90 break;
 91 }
 92
 93 if (isFunction)
 94 titleFragment.append(")");
 95
 96 if (hasMissingParameter)
 97 classNames.push("missing");
 98 } else if (isInitialState)
 99 classNames.push("initial-state");
 100
 101 const subtitle = null;
 102 super(classNames, titleFragment, subtitle, representedObject);
 103
 104 this._index = index;
 105 }
 106
 107 // Public
 108
 109 get index() { return this._index; }
 110
 111 get filterableData()
 112 {
 113 let text = [];
 114
 115 function getText(stringOrElement) {
 116 if (typeof stringOrElement === "string")
 117 text.push(stringOrElement);
 118 else if (stringOrElement instanceof Node)
 119 text.push(stringOrElement.textContent);
 120 }
 121
 122 getText(this._mainTitleElement || this._mainTitle);
 123 getText(this._subtitleElement || this._subtitle);
 124
 125 return {text};
 126 }
 127
 128 // Protected
 129
 130 onattach()
 131 {
 132 super.onattach();
 133
 134 this.element.dataset.index = this._index.toLocaleString();
 135 }
 136
 137 populateContextMenu(contextMenu, event)
 138 {
 139 contextMenu.appendItem(WebInspector.UIString("Copy Action"), () => {
 140 InspectorFrontendHost.copyText("context." + this._mainTitleElement.textContent + ";");
 141 });
 142
 143 contextMenu.appendSeparator();
 144
 145 let callFrame = this.representedObject.trace[0];
 146 if (callFrame) {
 147 contextMenu.appendItem(WebInspector.UIString("Reveal in Resources Tab"), () => {
 148 WebInspector.showSourceCodeLocation(callFrame.sourceCodeLocation, {
 149 ignoreNetworkTab: true,
 150 ignoreSearchTab: true,
 151 });
 152 });
 153
 154 contextMenu.appendSeparator();
 155 }
 156
 157 super.populateContextMenu(contextMenu, event);
 158 }
 159};

Source/WebInspectorUI/UserInterface/Views/RecordingContentView.css

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26.content-view:not(.tab).recording {
 27 padding: 15px;
 28 background-color: hsl(0, 0%, 90%);
 29}
 30
 31.content-view:not(.tab).recording > .preview-container {
 32 display: flex;
 33 align-items: center;
 34 justify-content: center;
 35 position: relative;
 36 width: 100%;
 37 height: 100%;
 38}
 39
 40.content-view:not(.tab).recording canvas {
 41 max-width: 100%;
 42 max-height: 100%;
 43}
 44
 45.content-view:not(.tab).recording canvas.path {
 46 position: absolute;
 47 z-index: 10;
 48}

Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.RecordingContentView = class RecordingContentView extends WebInspector.ContentView
 27{
 28 constructor(representedObject)
 29 {
 30 console.assert(representedObject instanceof WebInspector.Recording);
 31
 32 super(representedObject);
 33
 34 this._index = NaN;
 35 this._snapshots = [];
 36 this._initialContent = null;
 37
 38 this.element.classList.add("recording", this.representedObject.type);
 39
 40 if (this.representedObject.type === WebInspector.Recording.Type.Canvas2D) {
 41 this._pathContext = null;
 42
 43 this._showPathButtonNavigationItem = new WebInspector.ActivateButtonNavigationItem("show-path", WebInspector.UIString("Show Path"), WebInspector.UIString("Hide Path"), "Images/PaintFlashing.svg", 13, 13);
 44 this._showPathButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._showPathButtonClicked, this);
 45 this._showPathButtonNavigationItem.activated = !!WebInspector.settings.showCanvasPath.value;
 46
 47 this._showGridButtonNavigationItem = new WebInspector.ActivateButtonNavigationItem("show-grid", WebInspector.UIString("Show Grid"), WebInspector.UIString("Hide Grid"), "Images/NavigationItemCheckers.svg", 13, 13);
 48 this._showGridButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._showGridButtonClicked, this);
 49 this._showGridButtonNavigationItem.activated = !!WebInspector.settings.showImageGrid.value;
 50 }
 51
 52 this._previewContainer = this.element.appendChild(document.createElement("div"));
 53 this._previewContainer.classList.add("preview-container");
 54}
 55
 56 // Public
 57
 58 get navigationItems()
 59 {
 60 if (this.representedObject.type === WebInspector.Recording.Type.Canvas2D)
 61 return [this._showPathButtonNavigationItem, this._showGridButtonNavigationItem];
 62 return [];
 63 }
 64
 65 updateActionIndex(index, options = {})
 66 {
 67 console.assert(!this.representedObject || (index >= 0 && index < this.representedObject.actions.length));
 68 if (!this.representedObject || index < 0 || index >= this.representedObject.actions.length || index === this._index)
 69 return;
 70
 71 this._index = index;
 72
 73 if (this.representedObject.type === WebInspector.Recording.Type.Canvas2D)
 74 this._generateContentCanvas2D(index, options);
 75 }
 76
 77 // Protected
 78
 79 shown()
 80 {
 81 super.shown();
 82
 83 if (this.representedObject.type === WebInspector.Recording.Type.Canvas2D) {
 84 this._updateCanvasPath();
 85 this._updateImageGrid();
 86 }
 87 }
 88
 89 get supplementalRepresentedObjects()
 90 {
 91 let supplementalRepresentedObjects = super.supplementalRepresentedObjects;
 92 if (this.representedObject)
 93 supplementalRepresentedObjects.push(this.representedObject);
 94 return supplementalRepresentedObjects;
 95 }
 96
 97 // Private
 98
 99 _generateContentCanvas2D(index, options = {})
 100 {
 101 function actionAffectsPath(action) {
 102 const pathActionNames = ["arc", "arcTo", "beginPath", "bezierCurveTo", "closePath", "ellipse", "lineTo", "moveTo", "quadraticCurveTo", "rect", "resetTransform", "rotate", "scale", "setTransform", "transform", "translate"];
 103 return pathActionNames.includes(action.name);
 104 }
 105
 106 let imageLoad = (event) => {
 107 // Loading took too long and the current action index has already changed.
 108 if (index !== this._index)
 109 return;
 110
 111 this._generateContentCanvas2D(index, options);
 112 };
 113
 114
 115 let initialState = this.representedObject.initialState;
 116 if (initialState.content && !this._initialContent) {
 117 this._initialContent = new Image;
 118 this._initialContent.src = initialState.content;
 119 this._initialContent.addEventListener("load", imageLoad);
 120 return;
 121 }
 122
 123 let snapshotIndex = Math.floor(index / WebInspector.RecordingContentView._snapshotInterval);
 124 let snapshot = this._snapshots[snapshotIndex];
 125
 126 let indexStartVisualizingPath = Infinity;
 127 let saveCount = 0;
 128
 129 let actions = this.representedObject.actions;
 130 let applyActions = (from, to) => {
 131 if (snapshot.content) {
 132 snapshot.context.clearRect(0, 0, snapshot.element.width, snapshot.element.height);
 133 snapshot.context.drawImage(snapshot.content, 0, 0);
 134 }
 135
 136 for (let name in snapshot.state) {
 137 if (!(name in snapshot.context))
 138 continue;
 139
 140 try {
 141 if (WebInspector.RecordingAction.functionForType(this.representedObject.type, name))
 142 snapshot.context[name](...snapshot.state[name]);
 143 else
 144 snapshot.context[name] = snapshot.state[name];
 145 } catch (e) {
 146 delete snapshot.state[name];
 147 }
 148 }
 149
 150 let showPath = WebInspector.settings.showCanvasPath.value && to >= snapshot.index;
 151 if (showPath) {
 152 if (!this._pathContext) {
 153 let pathCanvas = document.createElement("canvas");
 154 pathCanvas.classList.add("path");
 155 this._pathContext = pathCanvas.getContext("2d");
 156 }
 157
 158 this._pathContext.canvas.width = snapshot.element.width;
 159 this._pathContext.canvas.height = snapshot.element.height;
 160 this._pathContext.clearRect(0, 0, snapshot.element.width, snapshot.element.height);
 161
 162 this._pathContext.save();
 163
 164 this._pathContext.fillStyle = "hsla(0, 0%, 100%, 0.75)";
 165 this._pathContext.fillRect(0, 0, snapshot.element.width, snapshot.element.height);
 166 }
 167
 168 for (let i = from; i <= to; ++i) {
 169 if (actions[i].name === "save")
 170 ++saveCount;
 171 else if (actions[i].name === "restore") {
 172 if (!saveCount) // Only attempt to restore if save has been called.
 173 continue;
 174 --saveCount;
 175 }
 176
 177 this._applyAction(snapshot.context, actions[i]);
 178
 179 if (showPath && i >= indexStartVisualizingPath && actionAffectsPath(actions[i])) {
 180 if (i === indexStartVisualizingPath)
 181 this._pathContext.setTransform(snapshot.context.getTransform());
 182
 183 this._applyAction(this._pathContext, actions[i], {nameOverride: actions[i].name === "moveTo" ? "lineTo" : null});
 184 }
 185 }
 186
 187 if (showPath) {
 188 this._pathContext.stroke();
 189 this._pathContext.restore();
 190 this._previewContainer.appendChild(this._pathContext.canvas);
 191 } else if (this._pathContext)
 192 this._pathContext.canvas.remove();
 193 };
 194
 195 if (!snapshot) {
 196 snapshot = this._snapshots[snapshotIndex] = {};
 197 snapshot.index = snapshotIndex * WebInspector.RecordingContentView._snapshotInterval;
 198 while (snapshot.index && actions[snapshot.index].name !== "beginPath")
 199 --snapshot.index;
 200
 201 snapshot.element = document.createElement("canvas");
 202 snapshot.context = snapshot.element.getContext("2d", ...initialState.parameters);
 203 if ("width" in initialState.attributes)
 204 snapshot.element.width = initialState.attributes.width;
 205 if ("height" in initialState.attributes)
 206 snapshot.element.height = initialState.attributes.height;
 207
 208 let lastSnapshot = snapshotIndex;
 209 while (--lastSnapshot >= 0) {
 210 if (this._snapshots[--lastSnapshot])
 211 break;
 212 }
 213
 214 let startIndex = 0;
 215 if (lastSnapshot < 0) {
 216 snapshot.content = this._initialContent;
 217 snapshot.state = {};
 218
 219 for (let key in initialState.attributes) {
 220 let value = initialState.attributes[key];
 221 if (key === "strokeStyle" || key === "fillStyle")
 222 value = this.representedObject.swizzle(value, WebInspector.Recording.Swizzle.CanvasStyle);
 223
 224 if (value === WebInspector.Recording.Swizzle.Invalid)
 225 continue;
 226
 227 snapshot.state[key] = value;
 228 }
 229 } else {
 230 snapshot.content = this._snapshots[lastSnapshot].content;
 231 snapshot.state = this._snapshots[lastSnapshot].state;
 232 startIndex = this._snapshots[lastSnapshot].index;
 233 }
 234
 235 saveCount = 0;
 236 snapshot.context.save();
 237
 238 applyActions(startIndex, snapshot.index - 1);
 239
 240 snapshot.state = {
 241 direction: snapshot.context.direction,
 242 fillStyle: snapshot.context.fillStyle,
 243 font: snapshot.context.font,
 244 globalAlpha: snapshot.context.globalAlpha,
 245 globalCompositeOperation: snapshot.context.globalCompositeOperation,
 246 imageSmoothingEnabled: snapshot.context.imageSmoothingEnabled,
 247 imageSmoothingQuality: snapshot.context.imageSmoothingQuality,
 248 lineCap: snapshot.context.lineCap,
 249 lineDashOffset: snapshot.context.lineDashOffset,
 250 lineJoin: snapshot.context.lineJoin,
 251 lineWidth: snapshot.context.lineWidth,
 252 miterLimit: snapshot.context.miterLimit,
 253 setLineDash: [snapshot.context.getLineDash()],
 254 setTransform: [snapshot.context.getTransform()],
 255 shadowBlur: snapshot.context.shadowBlur,
 256 shadowColor: snapshot.context.shadowColor,
 257 shadowOffsetX: snapshot.context.shadowOffsetX,
 258 shadowOffsetY: snapshot.context.shadowOffsetY,
 259 strokeStyle: snapshot.context.strokeStyle,
 260 textAlign: snapshot.context.textAlign,
 261 textBaseline: snapshot.context.textBaseline,
 262 webkitImageSmoothingEnabled: snapshot.context.webkitImageSmoothingEnabled,
 263 webkitLineDash: snapshot.context.webkitLineDash,
 264 webkitLineDashOffset: snapshot.context.webkitLineDashOffset,
 265 };
 266
 267 snapshot.context.restore();
 268 while (saveCount-- > 0)
 269 snapshot.context.restore();
 270
 271 snapshot.content = new Image;
 272 snapshot.content.src = snapshot.element.toDataURL();
 273 snapshot.content.addEventListener("load", imageLoad);
 274 return;
 275 }
 276
 277 for (let snapshot of this._snapshots) {
 278 if (snapshot)
 279 snapshot.element.remove();
 280 }
 281
 282 if (WebInspector.settings.showCanvasPath.value) {
 283 indexStartVisualizingPath = this._index;
 284 while (indexStartVisualizingPath > snapshot.index && actions[indexStartVisualizingPath].name !== "beginPath")
 285 --indexStartVisualizingPath;
 286 }
 287
 288 saveCount = 0;
 289 snapshot.context.save();
 290
 291 applyActions(snapshot.index, this._index);
 292 if (options.onCompleteCallback)
 293 options.onCompleteCallback(snapshot.context);
 294
 295 snapshot.context.restore();
 296 while (saveCount-- > 0)
 297 snapshot.context.restore();
 298
 299 this._previewContainer.appendChild(snapshot.element);
 300 this._updateImageGrid();
 301 }
 302
 303 _applyAction(context, action, options = {})
 304 {
 305 if (!action.valid)
 306 return;
 307
 308 if (action.parameters.includes(WebInspector.Recording.Swizzle.Invalid))
 309 return;
 310
 311 try {
 312 let name = options.nameOverride || action.name;
 313 if (action.functionForType(this.representedObject.type))
 314 context[name](...action.parameters);
 315 else {
 316 if (action.getterForType(this.representedObject.type))
 317 context[name];
 318 else
 319 context[name] = action.parameters[0];
 320 }
 321 } catch (e) {
 322 WebInspector.Recording.synthesizeError(WebInspector.UIString("“%s” threw error").format(action.name));
 323
 324 action.valid = false;
 325 }
 326 }
 327
 328 _updateCanvasPath()
 329 {
 330 let activated = WebInspector.settings.showCanvasPath.value;
 331 if (this._showPathButtonNavigationItem.activated !== activated)
 332 this._generateContentCanvas2D(this._index);
 333
 334 this._showPathButtonNavigationItem.activated = activated;
 335 }
 336
 337 _updateImageGrid()
 338 {
 339 let activated = WebInspector.settings.showImageGrid.value;
 340 this._showGridButtonNavigationItem.activated = activated;
 341
 342 let snapshotIndex = Math.floor(this._index / WebInspector.RecordingContentView._snapshotInterval);
 343 if (!isNaN(this._index) && this._snapshots[snapshotIndex])
 344 this._snapshots[snapshotIndex].element.classList.toggle("show-grid", activated);
 345 }
 346
 347 _showPathButtonClicked(event)
 348 {
 349 WebInspector.settings.showCanvasPath.value = !this._showPathButtonNavigationItem.activated;
 350
 351 this._updateCanvasPath();
 352 }
 353
 354 _showGridButtonClicked(event)
 355 {
 356 WebInspector.settings.showImageGrid.value = !this._showGridButtonNavigationItem.activated;
 357
 358 this._updateImageGrid();
 359 }
 360};
 361
 362WebInspector.RecordingContentView._snapshotInterval = 5000;

Source/WebInspectorUI/UserInterface/Views/RecordingNavigationSidebarPanel.css

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26.sidebar > .panel.navigation.recording > :matches(.content, .empty-content-placeholder) {
 27 top: var(--navigation-bar-height);
 28}
 29
 30.sidebar > .panel.navigation.recording > .content > .tree-outline {
 31 min-height: 100%;
 32 background: var(--transparent-stripe-background-gradient);
 33}
 34
 35.sidebar > .panel.navigation.recording > .content > .tree-outline > .item.parent:not(.action, .selected).expanded {
 36 background-color: white;
 37}
 38
 39.sidebar > .panel.navigation.recording > .content > .tree-outline .item.action:not(.initial-state)::before {
 40 position: relative;
 41 top: 3px;
 42 content: attr(data-index);
 43 font-family: Menlo, monospace;
 44 text-align: end;
 45}
 46
 47body[dir=ltr] .sidebar > .panel.navigation.recording > .content > .tree-outline .item.action:not(.initial-state)::before {
 48 float: left;
 49 margin-right: var(--tree-outline-icon-margin-end);
 50 margin-left: 0;
 51}
 52
 53body[dir=rtl] .sidebar > .panel.navigation.recording > .content > .tree-outline .item.action:not(.initial-state)::before {
 54 float: right;
 55 margin-right: 0;
 56 margin-left: var(--tree-outline-icon-margin-end);
 57}
 58
 59.sidebar > .panel.navigation.recording > .content > .tree-outline[data-indent="2"] .item.action:not(.initial-state)::before {
 60 min-width: 1.2em;
 61}
 62
 63.sidebar > .panel.navigation.recording > .content > .tree-outline[data-indent="3"] .item.action:not(.initial-state)::before {
 64 min-width: 1.9em;
 65}
 66
 67.sidebar > .panel.navigation.recording > .content > .tree-outline[data-indent="4"] .item.action:not(.initial-state)::before {
 68 min-width: 3.1em;
 69}
 70
 71.sidebar > .panel.navigation.recording > .content > .tree-outline[data-indent="5"] .item.action:not(.initial-state)::before {
 72 min-width: 3.7em;
 73}
 74
 75.sidebar > .panel.navigation.recording > .content > .tree-outline[data-indent="6"] .item.action:not(.initial-state)::before {
 76 min-width: 4.2em;
 77}
 78
 79.sidebar > .panel.navigation.recording > .content > .tree-outline[data-indent="7"] .item.action:not(.initial-state)::before {
 80 min-width: 5.4em;
 81}
 82
 83.sidebar > .panel.navigation.recording > .content .action > .icon {
 84 content: url("../Images/Source.svg");
 85}
 86
 87.sidebar > .panel.navigation.recording > .content .action.function > .icon {
 88 content: url(../Images/Function.svg);
 89}
 90
 91.sidebar > .panel.navigation.recording > .content .action.attribute.getter > .icon {
 92 content: url(../Images/Eye.svg);
 93}
 94
 95.sidebar > .panel.navigation.recording > .content .tree-outline:matches(:focus, .force-focus) .action.attribute.getter.selected > .icon {
 96 filter: invert();
 97}
 98
 99.sidebar > .panel.navigation.recording > .content .action.attribute.boolean > .icon {
 100 content: url(../Images/TypeBoolean.svg);
 101}
 102
 103.sidebar > .panel.navigation.recording > .content .action.attribute.number > .icon {
 104 content: url(../Images/TypeNumber.svg);
 105}
 106
 107.sidebar > .panel.navigation.recording > .content .action.attribute.object > .icon {
 108 content: url(../Images/TypeObject.svg);
 109}
 110
 111.sidebar > .panel.navigation.recording > .content .action.attribute.string > .icon {
 112 content: url(../Images/TypeString.svg);
 113}
 114
 115.sidebar > .panel.navigation.recording > .content > .tree-outline > .item.parent:not(.action) > .icon {
 116 content: url(../Images/RenderingFrame.svg);
 117}
 118
 119.sidebar > .panel.navigation.recording > .content .action:matches(.invalid, .missing) > .icon {
 120 filter: grayscale();
 121}
 122
 123body[dir=ltr] .sidebar > .panel.navigation.recording > .content .action:not(.initial-state) > .icon {
 124 margin-left: 0;
 125}
 126
 127body[dir=rtl] .sidebar > .panel.navigation.recording > .content .action:not(.initial-state) > .icon {
 128 margin-right: 0;
 129}
 130
 131.sidebar > .panel.navigation.recording > .content .action.visual:not(.selected, .invalid) {
 132 background-color: var(--value-visual-highlight);
 133}
 134
 135.sidebar > .panel.navigation.recording > .content .action:not(.selected, .initial-state) > .titles .parameter.swizzled {
 136 color: grey;
 137}
 138
 139.sidebar > .panel.navigation.recording > .content .action.invalid:not(.selected, .initial-state) > .titles .name,
 140.sidebar > .panel.navigation.recording > .content .action.missing:not(.selected, .initial-state) > .titles .parameter.swizzled.missing {
 141 color: red;
 142}

Source/WebInspectorUI/UserInterface/Views/RecordingNavigationSidebarPanel.js

 1/*
 2 * Copyright (C) 2015 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.RecordingNavigationSidebarPanel = class RecordingNavigationSidebarPanel extends WebInspector.NavigationSidebarPanel
 27{
 28 constructor()
 29 {
 30 super("recording", WebInspector.UIString("Recording"));
 31
 32 this.contentTreeOutline.customIndent = true;
 33
 34 this.filterBar.placeholder = WebInspector.UIString("Filter Actions");
 35
 36 this.recording = null;
 37
 38 this._importButton = null;
 39 this._exportButton = null;
 40 }
 41
 42 // Static
 43
 44 static disallowInstanceForClass()
 45 {
 46 return true;
 47 }
 48
 49 // Public
 50
 51 set recording(recording)
 52 {
 53 if (recording === this._recording)
 54 return;
 55
 56 this.contentTreeOutline.removeChildren();
 57
 58 this._recording = recording;
 59 if (this._recording) {
 60 this.contentTreeOutline.element.dataset.indent = Number.countDigits(this._recording.actions.length);
 61
 62 if (this._recording.actions[0] instanceof WebInspector.RecordingInitialStateAction)
 63 this.contentTreeOutline.appendChild(new WebInspector.RecordingActionTreeElement(this._recording.actions[0], 0, this._recording.type));
 64
 65 let cumulativeActionIndex = 1;
 66 this._recording.frames.forEach((frame, frameIndex) => {
 67 let folder = new WebInspector.FolderTreeElement(WebInspector.UIString("Frame %d").format((frameIndex + 1).toLocaleString()));
 68 this.contentTreeOutline.appendChild(folder);
 69
 70 for (let i = 0; i < frame.actions.length; ++i)
 71 folder.appendChild(new WebInspector.RecordingActionTreeElement(frame.actions[i], cumulativeActionIndex + i, this._recording.type));
 72
 73 if (frame.incomplete)
 74 folder.subtitle = WebInspector.UIString("Incomplete");
 75
 76 if (this._recording.frames.length === 1)
 77 folder.expand();
 78
 79 cumulativeActionIndex += frame.actions.length;
 80 });
 81 }
 82
 83 this.updateEmptyContentPlaceholder(WebInspector.UIString("No Recording Data"));
 84
 85 if (this._exportButton)
 86 this._exportButton.disabled = !this.contentTreeOutline.children.length;
 87 }
 88
 89 updateActionIndex(index, options = {})
 90 {
 91 console.assert(!this._recording || (index >= 0 && index < this._recording.actions.length));
 92 if (!this._recording || index < 0 || index >= this._recording.actions.length)
 93 return;
 94
 95 let treeOutline = this.contentTreeOutline;
 96 if (!(this._recording.actions[0] instanceof WebInspector.RecordingInitialStateAction) || index) {
 97 treeOutline = treeOutline.children[0];
 98 while (index > treeOutline.children.length) {
 99 index -= treeOutline.children.length;
 100 treeOutline = treeOutline.nextSibling;
 101 }
 102
 103 // Must subtract one from the final result since the initial state is considered index 0.
 104 --index;
 105 }
 106
 107 let treeElementToSelect = treeOutline.children[index];
 108 if (treeElementToSelect.parent && !treeElementToSelect.parent.expanded)
 109 treeElementToSelect = treeElementToSelect.parent;
 110
 111 const omitFocus = false;
 112 const selectedByUser = false;
 113 let suppressOnSelect = !(treeElementToSelect instanceof WebInspector.FolderTreeElement);
 114 const suppressOnDeselect = true;
 115 treeElementToSelect.revealAndSelect(omitFocus, selectedByUser, suppressOnSelect, suppressOnDeselect);
 116 }
 117
 118 // Protected
 119
 120 initialLayout()
 121 {
 122 super.initialLayout();
 123
 124 const role = "button";
 125
 126 const importLabel = WebInspector.UIString("Import");
 127 let importNavigationItem = new WebInspector.NavigationItem("recording-import", role, importLabel);
 128
 129 this._importButton = importNavigationItem.element.appendChild(document.createElement("button"));
 130 this._importButton.textContent = importLabel;
 131 this._importButton.addEventListener("click", this._importNavigationItemClicked.bind(this));
 132
 133 const exportLabel = WebInspector.UIString("Export");
 134 let exportNavigationItem = new WebInspector.NavigationItem("recording-export", role, exportLabel);
 135
 136 this._exportButton = exportNavigationItem.element.appendChild(document.createElement("button"));
 137 this._exportButton.textContent = exportLabel;
 138 this._exportButton.disabled = !this.contentTreeOutline.children.length;
 139 this._exportButton.addEventListener("click", this._exportNavigationItemClicked.bind(this));
 140
 141 const element = null;
 142 this.addSubview(new WebInspector.NavigationBar(element, [importNavigationItem, exportNavigationItem]));
 143 }
 144
 145 // Private
 146
 147 _importNavigationItemClicked(event)
 148 {
 149 WebInspector.loadDataFromFile((data) => {
 150 let payload = null;
 151 try {
 152 payload = JSON.parse(data);
 153 } catch (e) {
 154 WebInspector.Recording.synthesizeError(e);
 155 return;
 156 }
 157
 158 this.dispatchEventToListeners(WebInspector.RecordingNavigationSidebarPanel.Event.Import, {payload});
 159 });
 160 }
 161
 162 _exportNavigationItemClicked(event)
 163 {
 164 if (!this._recording)
 165 return;
 166
 167 const forceSaveAs = true;
 168 WebInspector.saveDataToFile({
 169 url: "web-inspector:///Recording.json",
 170 content: JSON.stringify(this._recording.toJSON()),
 171 }, forceSaveAs);
 172 }
 173};
 174
 175WebInspector.RecordingNavigationSidebarPanel.Event = {
 176 Import: "recording-navigation-sidebar-panel-import",
 177};

Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.css

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26.sidebar > .panel.details.recording-state > .content > .data-grid {
 27 min-height: 100%;
 28}
 29
 30.sidebar > .panel.details.recording-state > .content > .data-grid tr.modified {
 31 background-color: var(--value-changed-highlight);
 32}
 33
 34.sidebar > .panel.details.recording-state > .content > .data-grid tr:not(.selected) .unavailable {
 35 color: grey;
 36}

Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.js

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.RecordingStateDetailsSidebarPanel = class RecordingStateDetailsSidebarPanel extends WebInspector.DetailsSidebarPanel
 27{
 28 constructor()
 29 {
 30 super("recording-state", WebInspector.UIString("State"));
 31
 32 this._recording = null;
 33 this._index = NaN;
 34
 35 this._dataGrid = null;
 36 }
 37
 38 // Static
 39
 40 static disallowInstanceForClass()
 41 {
 42 return true;
 43 }
 44
 45 // Public
 46
 47 inspect(objects)
 48 {
 49 if (!(objects instanceof Array))
 50 objects = [objects];
 51
 52 this.recording = objects.find((object) => object instanceof WebInspector.Recording && object.type === WebInspector.Recording.Type.Canvas2D);
 53
 54 return !!this._recording;
 55 }
 56
 57 set recording(recording)
 58 {
 59 if (recording === this._recording)
 60 return;
 61
 62 this._recording = recording;
 63 this._index = NaN;
 64
 65 for (let subview of this.contentView.subviews)
 66 this.contentView.removeSubview(subview);
 67 }
 68
 69 updateActionIndex(index, context, options = {})
 70 {
 71 console.assert(!this._recording || (index >= 0 && index < this._recording.actions.length));
 72 if (!this._recording || index < 0 || index > this._recording.actions.length || index === this._index)
 73 return;
 74
 75 this._index = index;
 76
 77 if (this._recording.type === WebInspector.Recording.Type.Canvas2D)
 78 this._generateDetailsCanvas2D(context, options);
 79
 80 this.updateLayoutIfNeeded();
 81 }
 82
 83 // Private
 84
 85 _generateDetailsCanvas2D(context, options = {})
 86 {
 87 if (!this._dataGrid) {
 88 this._dataGrid = new WebInspector.DataGrid({
 89 name: {title: WebInspector.UIString("Name")},
 90 value: {title: WebInspector.UIString("Value")},
 91 });
 92 }
 93 if (!this._dataGrid.parentView)
 94 this.contentView.addSubview(this._dataGrid);
 95
 96 this._dataGrid.removeChildren();
 97
 98 if (!context)
 99 return;
 100
 101 let state = {
 102 direction: context.direction,
 103 fillStyle: context.fillStyle,
 104 font: context.font,
 105 globalAlpha: context.globalAlpha,
 106 globalCompositeOperation: context.globalCompositeOperation,
 107 imageSmoothingEnabled: context.imageSmoothingEnabled,
 108 imageSmoothingQuality: context.imageSmoothingQuality,
 109 lineCap: context.lineCap,
 110 lineDash: context.getLineDash(),
 111 lineDashOffset: context.lineDashOffset,
 112 lineJoin: context.lineJoin,
 113 lineWidth: context.lineWidth,
 114 miterLimit: context.miterLimit,
 115 shadowBlur: context.shadowBlur,
 116 shadowColor: context.shadowColor,
 117 shadowOffsetX: context.shadowOffsetX,
 118 shadowOffsetY: context.shadowOffsetY,
 119 strokeStyle: context.strokeStyle,
 120 textAlign: context.textAlign,
 121 textBaseline: context.textBaseline,
 122 transform: context.getTransform(),
 123 webkitImageSmoothingEnabled: context.webkitImageSmoothingEnabled,
 124 webkitLineDash: context.webkitLineDash,
 125 webkitLineDashOffset: context.webkitLineDashOffset,
 126 };
 127
 128 let action = this._recording.actions[this._index];
 129 let isGetter = action.getterForType(this._recording.type);
 130 let initialStateModifiers = action.initialStateModifiersForType(this._recording.type);
 131 for (let name in state) {
 132 let value = state[name];
 133 if (typeof value === "object") {
 134 let isGradient = value instanceof CanvasGradient;
 135 let isPattern = value instanceof CanvasPattern;
 136 if (isGradient || isPattern) {
 137 value = document.createElement("span");
 138 value.classList.add("unavailable");
 139 if (isGradient)
 140 value.textContent = WebInspector.unlocalizedString("CanvasGradient");
 141 else if (isPattern)
 142 value.textContent = WebInspector.unlocalizedString("CanvasPattern");
 143 } else {
 144 if (value instanceof DOMMatrix)
 145 value = [value.a, value.b, value.c, value.d, value.e, value.f];
 146
 147 value = JSON.stringify(value);
 148 }
 149 }
 150
 151 let classNames = [];
 152 if (!isGetter && initialStateModifiers.includes(name))
 153 classNames.push("modified");
 154
 155 const hasChildren = false;
 156 this._dataGrid.appendChild(new WebInspector.DataGridNode({name, value}, hasChildren, classNames));
 157 }
 158 }
 159};

Source/WebInspectorUI/UserInterface/Views/RecordingTabContentView.js

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.RecordingTabContentView = class RecordingTabContentView extends WebInspector.ContentBrowserTabContentView
 27{
 28 constructor()
 29 {
 30 let {image, title} = WebInspector.RecordingTabContentView.tabInfo();
 31 let tabBarItem = new WebInspector.GeneralTabBarItem(image, title);
 32
 33 const navigationSidebarPanelConstructor = WebInspector.RecordingNavigationSidebarPanel;
 34 const detailsSidebarPanelConstructors = [WebInspector.RecordingStateDetailsSidebarPanel, WebInspector.RecordingTraceDetailsSidebarPanel, WebInspector.CanvasDetailsSidebarPanel];
 35 const disableBackForward = true;
 36 let flexibleNavigationItem = new WebInspector.ScrubberNavigationItem;
 37 super("recording", "recording", tabBarItem, navigationSidebarPanelConstructor, detailsSidebarPanelConstructors, disableBackForward, flexibleNavigationItem);
 38
 39 this._visualActionIndexes = [];
 40
 41 this._scrubberNavigationItem = flexibleNavigationItem;
 42 this._scrubberNavigationItem.value = 0;
 43 this._scrubberNavigationItem.disabled = true;
 44 this._scrubberNavigationItem.addEventListener(WebInspector.ScrubberNavigationItem.Event.ValueChanged, this._scrubberNavigationItemValueChanged, this);
 45
 46 this.navigationSidebarPanel.addEventListener(WebInspector.RecordingNavigationSidebarPanel.Event.Import, this._navigationSidebarImport, this);
 47 this.navigationSidebarPanel.contentTreeOutline.addEventListener(WebInspector.TreeOutline.Event.SelectionDidChange, this._navigationSidebarTreeOutlineSelectionChanged, this);
 48
 49 this._recording = null;
 50 }
 51
 52 // Static
 53
 54 static tabInfo()
 55 {
 56 return {
 57 image: "Images/Recording.svg",
 58 title: WebInspector.UIString("Recording"),
 59 };
 60 }
 61
 62 static shouldSaveTab()
 63 {
 64 return false;
 65 }
 66
 67 // Public
 68
 69 get type()
 70 {
 71 return WebInspector.RecordingTabContentView.Type;
 72 }
 73
 74 canShowRepresentedObject(representedObject)
 75 {
 76 // Once a recording has been loaded for this tab, do not allow another one to be loaded.
 77 // This will cause new tabs to be opened for each recording, which is the desired behavior.
 78 if (this._recording)
 79 return false;
 80
 81 return representedObject instanceof WebInspector.Recording;
 82 }
 83
 84 showRepresentedObject(representedObject, cookie)
 85 {
 86 super.showRepresentedObject(representedObject, cookie);
 87
 88 this._recording = representedObject;
 89
 90 this._visualActionIndexes = [];
 91 representedObject.actions.forEach((action, i) => {
 92 if (action.visualForType(representedObject.type))
 93 this._visualActionIndexes.push(i);
 94 });
 95
 96 this._scrubberNavigationItem.value = 0;
 97 this._scrubberNavigationItem.min = 0;
 98 this._scrubberNavigationItem.max = representedObject.actions.length - 1;
 99 this._scrubberNavigationItem.disabled = false;
 100
 101 this.navigationSidebarPanel.recording = representedObject;
 102
 103 this._updateActionIndex(this._scrubberNavigationItem.value);
 104 }
 105
 106 // Protected
 107
 108 restoreStateFromCookie(restorationType)
 109 {
 110 // Don't attempt to do anything to this tab.
 111 }
 112
 113 saveStateToCookie(cookie)
 114 {
 115 // Don't attempt to do anything to this tab.
 116 }
 117
 118 closed()
 119 {
 120 super.closed();
 121
 122 for (let detailsSidebarPanel of this.detailsSidebarPanels)
 123 detailsSidebarPanel.recording = null;
 124
 125 this.navigationSidebarPanel.recording = null;
 126 }
 127
 128 // Private
 129
 130 _updateActionIndex(index, options = {})
 131 {
 132 this._scrubberNavigationItem.value = index;
 133
 134 options.onCompleteCallback = (context) => {
 135 for (let detailsSidebarPanel of this.detailsSidebarPanels) {
 136 if (detailsSidebarPanel.updateActionIndex)
 137 detailsSidebarPanel.updateActionIndex(index, context, options);
 138 }
 139 };
 140
 141 this.contentBrowser.currentContentView.updateActionIndex(index, options);
 142
 143 // This must be placed last, as it is possible for the update of the NavigationSidebarPanel
 144 // to trigger another update of the index, such as if the selected index is not expanded.
 145 if (!options.suppressNavigationUpdate)
 146 this.navigationSidebarPanel.updateActionIndex(index, options);
 147 }
 148
 149 _scrubberNavigationItemValueChanged(event)
 150 {
 151 for (let i = 0; i <= this._visualActionIndexes.length; ++i) {
 152 if (this._visualActionIndexes[i] < this._scrubberNavigationItem.value)
 153 continue;
 154
 155 let min = i ? this._visualActionIndexes[i - 1] : this._scrubberNavigationItem.min;
 156 let max = i < this._visualActionIndexes.length ? this._visualActionIndexes[i] : this._scrubberNavigationItem.max;
 157 this._updateActionIndex(this._scrubberNavigationItem.value >= (min + max) / 2 ? max : min);
 158 return;
 159 }
 160 }
 161
 162 _navigationSidebarImport(event)
 163 {
 164 let recording = WebInspector.Recording.fromPayload(event.data.payload);
 165 if (!recording) {
 166 WebInspector.Recording.synthesizeError(WebInspector.UIString("unsupported version"));
 167 return;
 168 }
 169
 170 this.showRepresentedObject(recording);
 171 }
 172
 173 _navigationSidebarTreeOutlineSelectionChanged(event)
 174 {
 175 // Ignore deselect events.
 176 if (!event.data.selectedElement)
 177 return;
 178
 179 let options = {suppressNavigationUpdate: true};
 180 let selectedTreeElement = this.navigationSidebarPanel.contentTreeOutline.selectedTreeElement;
 181 if (selectedTreeElement instanceof WebInspector.FolderTreeElement)
 182 selectedTreeElement = selectedTreeElement.children.lastValue;
 183 this._updateActionIndex(selectedTreeElement.index, options);
 184 }
 185};
 186
 187WebInspector.RecordingTabContentView.Type = "recording";

Source/WebInspectorUI/UserInterface/Views/RecordingTraceDetailsSidebarPanel.css

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26.sidebar > .panel.details.recording-trace > .content > .call-frame {
 27 height: 20px;
 28 padding: 2px 5px;
 29}
 30
 31.sidebar > .details.recording-trace > .content > .no-trace-data {
 32 display: flex;
 33 justify-content: center;
 34 align-items: center;
 35 position: absolute;
 36 top: 0;
 37 right: 0;
 38 bottom: 0;
 39 left: 0;
 40 padding: 0;
 41 overflow: hidden;
 42}
 43
 44.sidebar > .details.recording-trace > .content > .no-trace-data > .message {
 45 display: inline-block;
 46 padding: 5px 15px 6px;
 47 font-size: 13px;
 48 white-space: nowrap;
 49 color: white;
 50 text-shadow: hsla(0, 0%, 0%, 0.2) 0 1px 0;
 51 background-color: hsla(0, 0%, 0%, 0.15);
 52 box-shadow: inset hsla(0, 0%, 0%, 0.2) 0 1px 0, hsla(0, 0%, 100%, 0.4) 0 1px 0;
 53 border-radius: 6px;
 54}

Source/WebInspectorUI/UserInterface/Views/RecordingTraceDetailsSidebarPanel.js

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.RecordingTraceDetailsSidebarPanel = class RecordingTraceDetailsSidebarPanel extends WebInspector.DetailsSidebarPanel
 27{
 28 constructor()
 29 {
 30 super("recording-trace", WebInspector.UIString("Trace"));
 31
 32 this._recording = null;
 33 this._index = NaN;
 34 }
 35
 36 // Static
 37
 38 static disallowInstanceForClass()
 39 {
 40 return true;
 41 }
 42
 43 // Public
 44
 45 inspect(objects)
 46 {
 47 if (!(objects instanceof Array))
 48 objects = [objects];
 49
 50 this.recording = objects.find((object) => object instanceof WebInspector.Recording);
 51
 52 return !!this._recording;
 53 }
 54
 55 set recording(recording)
 56 {
 57 if (recording === this._recording)
 58 return;
 59
 60 this._recording = recording;
 61 this._index = NaN;
 62
 63 this.contentView.element.removeChildren();
 64 }
 65
 66 updateActionIndex(index, context, options = {})
 67 {
 68 console.assert(!this._recording || (index >= 0 && index < this._recording.actions.length));
 69 if (!this._recording || index < 0 || index > this._recording.actions.length || index === this._index)
 70 return;
 71
 72 this._index = index;
 73
 74 this.contentView.element.removeChildren();
 75
 76 let trace = this._recording.actions[this._index].trace;
 77 if (!trace.length) {
 78 let noTraceDataElement = this.contentView.element.appendChild(document.createElement("div"));
 79 noTraceDataElement.classList.add("no-trace-data");
 80
 81 let noTraceDataMessageElement = noTraceDataElement.appendChild(document.createElement("div"));
 82 noTraceDataMessageElement.classList.add("message");
 83 noTraceDataMessageElement.textContent = WebInspector.UIString("No Trace Data");
 84 return;
 85 }
 86
 87 const showFunctionName = true;
 88 for (let callFrame of trace)
 89 this.contentView.element.appendChild(new WebInspector.CallFrameView(callFrame, showFunctionName));
 90 }
 91};

Source/WebInspectorUI/UserInterface/Views/ScrubberNavigationItem.css

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26.navigation-bar .item.scrubber {
 27 padding: 0 4px;
 28}
 29
 30.navigation-bar .item.scrubber > input {
 31 width: 100%;
 32}
 33
 34.navigation-bar .item.scrubber > input[disabled] {
 35 opacity: 0.5;
 36 pointer-events: none;
 37}

Source/WebInspectorUI/UserInterface/Views/ScrubberNavigationItem.js

 1/*
 2 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WebInspector.ScrubberNavigationItem = class ScrubberNavigationItem extends WebInspector.FlexibleSpaceNavigationItem
 27{
 28 constructor(identifier)
 29 {
 30 super(identifier);
 31
 32 this._sliderElement = this._element.appendChild(document.createElement("input"));
 33 this._sliderElement.type = "range";
 34 this._sliderElement.addEventListener("input", this._sliderChanged.bind(this));
 35 }
 36
 37 // Public
 38
 39 get value() { return parseInt(this._sliderElement.value); }
 40 set value(value) { this._sliderElement.value = value; }
 41
 42 get min() { return parseInt(this._sliderElement.min); }
 43 set min(min) { this._sliderElement.min = min; }
 44
 45 get max() { return parseInt(this._sliderElement.max); }
 46 set max(max) { this._sliderElement.max = max; }
 47
 48 set disabled(flag)
 49 {
 50 this._sliderElement.disabled = !!flag;
 51 }
 52
 53 // Protected
 54
 55 get additionalClassNames()
 56 {
 57 return super.additionalClassNames.concat(["scrubber"]);
 58 }
 59
 60 // Private
 61
 62 _sliderChanged(event)
 63 {
 64 this.dispatchEventToListeners(WebInspector.ScrubberNavigationItem.Event.ValueChanged);
 65 }
 66};
 67
 68WebInspector.ScrubberNavigationItem.Event = {
 69 ValueChanged: "slider-navigation-item-value-changed",
 70};

Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.css

@@body[dir=rtl] .content-view.settings > .settings-view > .container > .editor-gro
129129 margin-left: var(--settings-input-number-margin-end);
130130 margin-right: var(--settings-input-number-margin-start);
131131}
 132
 133.content-view.settings > .settings-view.experimental > .reload {
 134 position: absolute;
 135 right: 0;
 136 bottom: 0;
 137 left: 0;
 138 padding: 8px;
 139 text-align: center;
 140 background-color: white;
 141 border-top: 1px solid lightgrey;
 142}
 143
 144 .content-view.settings > .settings-view.experimental > .reload > button {
 145 display: block;
 146 margin: 8px auto 0;
 147}

Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js

@@WebInspector.SettingsTabContentView = class SettingsTabContentView extends WebIn
161161 this.addSubview(this._navigationBar);
162162
163163 this._createGeneralSettingsView();
 164 this._createExperimentalSettingsView();
164165
165166 WebInspector.notifications.addEventListener(WebInspector.Notification.DebugUIEnabledDidChange, this._updateDebugSettingsViewVisibility, this);
166167 this._updateDebugSettingsViewVisibility();

@@WebInspector.SettingsTabContentView = class SettingsTabContentView extends WebIn
226227 this.addSettingsView(generalSettingsView);
227228 }
228229
 230 _createExperimentalSettingsView()
 231 {
 232 if (!window.CanvasAgent)
 233 return;
 234
 235 let experimentalSettingsView = new WebInspector.SettingsView("experimental", WebInspector.UIString("Experimental"));
 236
 237 let reloadContainer = document.createElement("div");
 238 reloadContainer.classList.add("reload");
 239
 240 reloadContainer.append(WebInspector.UIString("Changes will take effect on the next load"));
 241
 242 let reloadButton = reloadContainer.appendChild(document.createElement("button"));
 243 reloadButton.textContent = WebInspector.UIString("Reload WebInspector");
 244 reloadButton.disabled = true;
 245 reloadButton.addEventListener("click", () => { window.location.reload(); });
 246
 247 function addSetting(group, setting, label) {
 248 let editor = group.addSetting(setting, label);
 249 editor.addEventListener(WebInspector.SettingEditor.Event.ValueDidChange, () => {
 250 reloadButton.disabled = false;
 251 });
 252 }
 253
 254 let canvasGroup = experimentalSettingsView.addGroup(WebInspector.UIString("Canvas:"));
 255 addSetting(canvasGroup, WebInspector.settings.experimentalShowCanvasContextsInResources, WebInspector.UIString("Show Contexts in Resources Tab"));
 256
 257 experimentalSettingsView.addSeparator();
 258
 259 experimentalSettingsView.element.appendChild(reloadContainer);
 260
 261 this.addSettingsView(experimentalSettingsView);
 262 }
 263
229264 _createDebugSettingsView()
230265 {
231266 if (this._debugSettingsView)

@@WebInspector.SettingsTabContentView = class SettingsTabContentView extends WebIn
260295 layoutDirectionEditor.value = WebInspector.settings.layoutDirection.value;
261296 layoutDirectionEditor.addEventListener(WebInspector.SettingEditor.Event.ValueDidChange, () => { WebInspector.setLayoutDirection(layoutDirectionEditor.value); });
262297
263  if (window.CanvasAgent) {
264  this._debugSettingsView.addSeparator();
265 
266  this._debugSettingsView.addSetting(WebInspector.UIString("Canvas:"), WebInspector.settings.experimentalShowCanvasContextsInResources, WebInspector.UIString("Show Contexts in Resources Tab"));
267  }
268 
269298 this.addSettingsView(this._debugSettingsView);
270299 }
271300

Source/WebInspectorUI/UserInterface/Views/TabContentView.js

@@WebInspector.TabContentView = class TabContentView extends WebInspector.ContentV
177177 {
178178 if (!this._navigationSidebarPanelConstructor)
179179 return null;
180  return WebInspector.instanceForClass(this._navigationSidebarPanelConstructor);
 180 if (!this._navigationSidebarPanel)
 181 this._navigationSidebarPanel = WebInspector.instanceForClass(this._navigationSidebarPanelConstructor);
 182 return this._navigationSidebarPanel;
181183 }
182184
183185 get navigationSidebarCollapsedSetting() { return this._navigationSidebarCollapsedSetting; }

Source/WebInspectorUI/UserInterface/Views/TimelineIcons.css

7474}
7575
7676.rendering-frame-record .icon {
77  content: url(../Images/TimelineRecordRenderingFrame.svg);
 77 content: url(../Images/RenderingFrame.svg);
7878}
7979
8080.api-record .icon {

Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.css

4141}
4242
4343.timeline.tab.content-view .navigation-bar > .item.radio {
44  margin-top: 5px;
4544 height: 17px;
4645}

Source/WebInspectorUI/UserInterface/Views/TimelineView.css

4242}
4343
4444.panel.navigation.timeline.timeline-recording-content-view-showing > .content > .tree-outline {
45  background-image: linear-gradient(to bottom, transparent, transparent 50%, hsla(0, 0%, 0%, 0.03) 50%, hsla(0, 0%, 0%, 0.03));
46  background-size: 100% 40px;
 45 background: var(--transparent-stripe-background-gradient);
4746}

Source/WebInspectorUI/UserInterface/Views/Variables.css

6161 --text-color-gray-medium: hsl(0, 0%, 50%);
6262 --error-text-color: hsl(0, 86%, 47%);
6363
 64 --value-changed-highlight: hsla(83, 100%, 48%, 0.4);
 65 --value-visual-highlight: hsla(60, 100%, 50%, 0.4);
 66
6467 --breakpoint-fill-color: hsl(212, 45%, 54%);
6568 --breakpoint-stroke-color: hsl(212, 45%, 48%);
6669 --breakpoint-disabled-fill-color: hsl(212, 40%, 84%);

9194
9295 --even-zebra-stripe-row-background-color: white;
9396 --odd-zebra-stripe-row-background-color: hsl(0, 0%, 95%);
 97 --transparent-stripe-background-gradient: linear-gradient(to bottom, transparent, transparent 50%, hsla(0, 0%, 0%, 0.03) 50%, hsla(0, 0%, 0%, 0.03)) top left / 100% 40px;
9498
9599 --toolbar-height: 36px;
96100 --tab-bar-height: 24px;

LayoutTests/inspector/canvas/recording-2d-expected.txt

 1Test that CanvasManager is able to record actions made to 2D canvas contexts.
 2
 3
 4== Running test suite: Canvas.recording2D
 5-- Running test case: Canvas.recording2D.singleFrame
 6{
 7 "version": 1,
 8 "type": "canvas-2d",
 9 "initialState": {
 10 "attributes": {
 11 "width": 2,
 12 "height": 2,
 13 "setTransform": [
 14 1,
 15 0,
 16 0,
 17 1,
 18 0,
 19 0
 20 ],
 21 "globalAlpha": 1,
 22 "globalCompositeOperation": "source-over",
 23 "lineWidth": 1,
 24 "lineCap": "butt",
 25 "lineJoin": "miter",
 26 "miterLimit": 10,
 27 "shadowOffsetX": 0,
 28 "shadowOffsetY": 0,
 29 "shadowBlur": 0,
 30 "shadowColor": "rgba(0, 0, 0, 0)",
 31 "setLineDash": [
 32 []
 33 ],
 34 "lineDashOffset": 0,
 35 "font": "10px sans-serif",
 36 "textAlign": "start",
 37 "textBaseline": "alphabetic",
 38 "direction": "ltr",
 39 "strokeStyle": 0,
 40 "fillStyle": 0,
 41 "imageSmoothingEnabled": true,
 42 "imageSmoothingQuality": "low"
 43 },
 44 "content": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAAAtJREFUCB1jYEAHAAASAAGAFMrMAAAAAElFTkSuQmCC"
 45 },
 46 "frames": [
 47 {
 48 "actions": [
 49 [
 50 1,
 51 [
 52 1,
 53 2,
 54 3,
 55 4,
 56 5,
 57 0
 58 ],
 59 [
 60 4,
 61 6,
 62 7,
 63 9,
 64 11,
 65 13,
 66 16,
 67 18,
 68 20
 69 ]
 70 ],
 71 [
 72 1,
 73 [
 74 6,
 75 7,
 76 8,
 77 9,
 78 10,
 79 1
 80 ],
 81 [
 82 21,
 83 6,
 84 22,
 85 9,
 86 11,
 87 13,
 88 16,
 89 18,
 90 20
 91 ]
 92 ]
 93 ]
 94 }
 95 ],
 96 "data": [
 97 "#000000",
 98 "arc",
 99 "",
 100 "/LayoutTests/inspector/canvas/recording-2d.html",
 101 [
 102 2,
 103 3,
 104 56,
 105 28
 106 ],
 107 "e",
 108 [
 109 5,
 110 3,
 111 44,
 112 12
 113 ],
 114 [
 115 2,
 116 3,
 117 56,
 118 14
 119 ],
 120 "f",
 121 [
 122 8,
 123 3,
 124 348,
 125 24
 126 ],
 127 "performActions",
 128 [
 129 10,
 130 3,
 131 352,
 132 6
 133 ],
 134 "global code",
 135 [
 136 12,
 137 2,
 138 1,
 139 15
 140 ],
 141 "evaluateWithScopeExtension",
 142 "[native code]",
 143 [
 144 14,
 145 15,
 146 0,
 147 0
 148 ],
 149 "_evaluateOn",
 150 [
 151 17,
 152 2,
 153 128,
 154 29
 155 ],
 156 "_evaluateAndWrap",
 157 [
 158 19,
 159 2,
 160 122,
 161 108
 162 ],
 163 [
 164 2,
 165 3,
 166 57,
 167 28
 168 ],
 169 [
 170 2,
 171 3,
 172 57,
 173 14
 174 ]
 175 ]
 176}
 177
 178-- Running test case: Canvas.recording2D.multipleFrames
 179{
 180 "version": 1,
 181 "type": "canvas-2d",
 182 "initialState": {
 183 "attributes": {
 184 "width": 2,
 185 "height": 2,
 186 "setTransform": [
 187 1,
 188 0,
 189 0,
 190 1,
 191 0,
 192 0
 193 ],
 194 "globalAlpha": 1,
 195 "globalCompositeOperation": "source-over",
 196 "lineWidth": 1,
 197 "lineCap": "butt",
 198 "lineJoin": "miter",
 199 "miterLimit": 10,
 200 "shadowOffsetX": 0,
 201 "shadowOffsetY": 0,
 202 "shadowBlur": 0,
 203 "shadowColor": "rgba(0, 0, 0, 0)",
 204 "setLineDash": [
 205 []
 206 ],
 207 "lineDashOffset": 0,
 208 "font": "10px sans-serif",
 209 "textAlign": "start",
 210 "textBaseline": "alphabetic",
 211 "direction": "ltr",
 212 "strokeStyle": 0,
 213 "fillStyle": 0,
 214 "imageSmoothingEnabled": true,
 215 "imageSmoothingQuality": "low"
 216 },
 217 "content": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAAAtJREFUCB1jYEAHAAASAAGAFMrMAAAAAElFTkSuQmCC"
 218 },
 219 "frames": [
 220 {
 221 "actions": [
 222 [
 223 1,
 224 [
 225 1,
 226 2,
 227 3,
 228 4,
 229 5,
 230 0
 231 ],
 232 [
 233 4,
 234 6,
 235 7,
 236 9,
 237 11,
 238 13,
 239 16,
 240 18,
 241 20
 242 ]
 243 ],
 244 [
 245 1,
 246 [
 247 6,
 248 7,
 249 8,
 250 9,
 251 10,
 252 1
 253 ],
 254 [
 255 21,
 256 6,
 257 22,
 258 9,
 259 11,
 260 13,
 261 16,
 262 18,
 263 20
 264 ]
 265 ]
 266 ]
 267 },
 268 {
 269 "actions": [
 270 [
 271 23,
 272 [
 273 1,
 274 2,
 275 3,
 276 4,
 277 5
 278 ],
 279 [
 280 24,
 281 6,
 282 25,
 283 9
 284 ]
 285 ]
 286 ]
 287 },
 288 {
 289 "actions": [
 290 [
 291 26,
 292 [],
 293 [
 294 27,
 295 9
 296 ]
 297 ]
 298 ]
 299 },
 300 {
 301 "actions": [
 302 [
 303 28,
 304 [
 305 1,
 306 2,
 307 3,
 308 4,
 309 5,
 310 6
 311 ],
 312 [
 313 29,
 314 9
 315 ]
 316 ]
 317 ]
 318 },
 319 {
 320 "actions": [
 321 [
 322 30,
 323 [
 324 1,
 325 2,
 326 3,
 327 4
 328 ],
 329 [
 330 31,
 331 9
 332 ]
 333 ]
 334 ]
 335 },
 336 {
 337 "actions": [
 338 [
 339 32,
 340 [],
 341 [
 342 33,
 343 9
 344 ]
 345 ]
 346 ]
 347 },
 348 {
 349 "actions": [
 350 [
 351 34,
 352 [
 353 35
 354 ],
 355 [
 356 36,
 357 9
 358 ]
 359 ],
 360 [
 361 34,
 362 [
 363 37
 364 ],
 365 [
 366 38,
 367 9
 368 ]
 369 ],
 370 [
 371 34,
 372 [
 373 39,
 374 35
 375 ],
 376 [
 377 40,
 378 9
 379 ]
 380 ],
 381 [
 382 34,
 383 [
 384 41,
 385 37
 386 ],
 387 [
 388 42,
 389 9
 390 ]
 391 ]
 392 ]
 393 },
 394 {
 395 "actions": [
 396 [
 397 43,
 398 [],
 399 [
 400 44,
 401 9
 402 ]
 403 ]
 404 ]
 405 },
 406 {
 407 "actions": [
 408 [
 409 45,
 410 [],
 411 [
 412 46,
 413 9
 414 ]
 415 ]
 416 ]
 417 },
 418 {
 419 "actions": [
 420 [
 421 47,
 422 [
 423 48
 424 ],
 425 [
 426 49,
 427 6,
 428 50,
 429 9
 430 ]
 431 ],
 432 [
 433 47,
 434 [
 435 2,
 436 3
 437 ],
 438 [
 439 51,
 440 6,
 441 52,
 442 9
 443 ]
 444 ]
 445 ]
 446 },
 447 {
 448 "actions": [
 449 [
 450 53,
 451 [
 452 1,
 453 2,
 454 3,
 455 4
 456 ],
 457 [
 458 54,
 459 6,
 460 55,
 461 9
 462 ]
 463 ]
 464 ]
 465 },
 466 {
 467 "actions": [
 468 [
 469 56,
 470 [
 471 57,
 472 58
 473 ],
 474 [
 475 59,
 476 6,
 477 60,
 478 9
 479 ]
 480 ],
 481 [
 482 56,
 483 [
 484 61,
 485 62
 486 ],
 487 [
 488 63,
 489 6,
 490 64,
 491 9
 492 ]
 493 ],
 494 [
 495 56,
 496 [
 497 65,
 498 66
 499 ],
 500 [
 501 67,
 502 6,
 503 68,
 504 9
 505 ]
 506 ]
 507 ]
 508 },
 509 {
 510 "actions": [
 511 [
 512 69,
 513 [
 514 1,
 515 2,
 516 3,
 517 4,
 518 5,
 519 6
 520 ],
 521 [
 522 70,
 523 6,
 524 71,
 525 9
 526 ]
 527 ]
 528 ]
 529 },
 530 {
 531 "actions": [
 532 [
 533 72,
 534 [
 535 73
 536 ],
 537 [
 538 74,
 539 9
 540 ]
 541 ]
 542 ]
 543 },
 544 {
 545 "actions": [
 546 [
 547 75,
 548 [
 549 76
 550 ],
 551 [
 552 77,
 553 9
 554 ]
 555 ],
 556 [
 557 75,
 558 [
 559 39,
 560 76
 561 ],
 562 [
 563 78,
 564 9
 565 ]
 566 ]
 567 ]
 568 },
 569 {
 570 "actions": [
 571 [
 572 79,
 573 [
 574 57,
 575 1,
 576 2
 577 ],
 578 [
 579 80,
 580 6,
 581 81,
 582 9
 583 ]
 584 ],
 585 [
 586 79,
 587 [
 588 61,
 589 3,
 590 4
 591 ],
 592 [
 593 82,
 594 6,
 595 83,
 596 9
 597 ]
 598 ],
 599 [
 600 79,
 601 [
 602 65,
 603 5,
 604 6
 605 ],
 606 [
 607 84,
 608 6,
 609 85,
 610 9
 611 ]
 612 ],
 613 [
 614 79,
 615 [
 616 57,
 617 7,
 618 8,
 619 9,
 620 10
 621 ],
 622 [
 623 86,
 624 6,
 625 87,
 626 9
 627 ]
 628 ],
 629 [
 630 79,
 631 [
 632 61,
 633 11,
 634 12,
 635 13,
 636 14
 637 ],
 638 [
 639 88,
 640 6,
 641 89,
 642 9
 643 ]
 644 ],
 645 [
 646 79,
 647 [
 648 65,
 649 15,
 650 16,
 651 17,
 652 18
 653 ],
 654 [
 655 90,
 656 6,
 657 91,
 658 9
 659 ]
 660 ],
 661 [
 662 79,
 663 [
 664 57,
 665 19,
 666 20,
 667 21,
 668 22,
 669 23,
 670 24,
 671 25,
 672 26
 673 ],
 674 [
 675 92,
 676 6,
 677 93,
 678 9
 679 ]
 680 ],
 681 [
 682 79,
 683 [
 684 61,
 685 27,
 686 28,
 687 29,
 688 30,
 689 31,
 690 32,
 691 33,
 692 34
 693 ],
 694 [
 695 94,
 696 6,
 697 95,
 698 9
 699 ]
 700 ],
 701 [
 702 79,
 703 [
 704 65,
 705 35,
 706 36,
 707 37,
 708 38,
 709 39,
 710 40,
 711 41,
 712 42
 713 ],
 714 [
 715 96,
 716 6,
 717 97,
 718 9
 719 ]
 720 ]
 721 ]
 722 },
 723 {
 724 "actions": [
 725 [
 726 98,
 727 [
 728 57,
 729 1,
 730 2,
 731 3,
 732 4,
 733 5,
 734 6,
 735 7,
 736 8,
 737 2
 738 ],
 739 [
 740 99,
 741 9
 742 ]
 743 ],
 744 [
 745 98,
 746 [
 747 57,
 748 9,
 749 10,
 750 11,
 751 12,
 752 13,
 753 14,
 754 15,
 755 16,
 756 73
 757 ],
 758 [
 759 100,
 760 9
 761 ]
 762 ]
 763 ]
 764 },
 765 {
 766 "actions": [
 767 [
 768 101,
 769 [
 770 1,
 771 2,
 772 3,
 773 4,
 774 5,
 775 6,
 776 7,
 777 0
 778 ],
 779 [
 780 102,
 781 6,
 782 103,
 783 9
 784 ]
 785 ],
 786 [
 787 101,
 788 [
 789 8,
 790 9,
 791 10,
 792 11,
 793 12,
 794 13,
 795 14,
 796 1
 797 ],
 798 [
 799 104,
 800 6,
 801 105,
 802 9
 803 ]
 804 ]
 805 ]
 806 },
 807 {
 808 "actions": [
 809 [
 810 106,
 811 [
 812 35
 813 ],
 814 [
 815 107,
 816 9
 817 ]
 818 ],
 819 [
 820 106,
 821 [
 822 37
 823 ],
 824 [
 825 108,
 826 9
 827 ]
 828 ],
 829 [
 830 106,
 831 [
 832 39,
 833 35
 834 ],
 835 [
 836 109,
 837 9
 838 ]
 839 ],
 840 [
 841 106,
 842 [
 843 41,
 844 37
 845 ],
 846 [
 847 110,
 848 9
 849 ]
 850 ]
 851 ]
 852 },
 853 {
 854 "actions": [
 855 [
 856 111,
 857 [
 858 1,
 859 2,
 860 3,
 861 4
 862 ],
 863 [
 864 112,
 865 9
 866 ]
 867 ]
 868 ]
 869 },
 870 {
 871 "actions": [
 872 [
 873 113,
 874 [
 875 73
 876 ],
 877 [
 878 114,
 879 9
 880 ]
 881 ],
 882 [
 883 113,
 884 [
 885 116
 886 ],
 887 [
 888 117,
 889 9
 890 ]
 891 ],
 892 [
 893 113,
 894 [
 895 119
 896 ],
 897 [
 898 120,
 899 9
 900 ]
 901 ],
 902 [
 903 113,
 904 [
 905 124
 906 ],
 907 [
 908 125,
 909 9
 910 ]
 911 ]
 912 ]
 913 },
 914 {
 915 "actions": [
 916 [
 917 126,
 918 [
 919 58,
 920 1,
 921 2
 922 ],
 923 [
 924 127,
 925 9
 926 ]
 927 ],
 928 [
 929 126,
 930 [
 931 62,
 932 3,
 933 4,
 934 5
 935 ],
 936 [
 937 128,
 938 9
 939 ]
 940 ]
 941 ]
 942 },
 943 {
 944 "actions": [
 945 [
 946 129,
 947 [
 948 73
 949 ],
 950 [
 951 130,
 952 9
 953 ]
 954 ]
 955 ]
 956 },
 957 {
 958 "actions": [
 959 [
 960 131,
 961 [
 962 1,
 963 2,
 964 3,
 965 4
 966 ],
 967 [
 968 132,
 969 6,
 970 133,
 971 9
 972 ]
 973 ]
 974 ]
 975 },
 976 {
 977 "actions": [
 978 [
 979 134,
 980 [],
 981 [
 982 135,
 983 9
 984 ]
 985 ]
 986 ]
 987 },
 988 {
 989 "actions": [
 990 [
 991 136,
 992 [],
 993 [
 994 137,
 995 9
 996 ]
 997 ]
 998 ]
 999 },
 1000 {
 1001 "actions": [
 1002 [
 1003 138,
 1004 [
 1005 0
 1006 ],
 1007 [
 1008 139,
 1009 9
 1010 ]
 1011 ]
 1012 ]
 1013 },
 1014 {
 1015 "actions": [
 1016 [
 1017 140,
 1018 [
 1019 73
 1020 ],
 1021 [
 1022 141,
 1023 9
 1024 ]
 1025 ]
 1026 ]
 1027 },
 1028 {
 1029 "actions": [
 1030 [
 1031 142,
 1032 [
 1033 1
 1034 ],
 1035 [
 1036 143,
 1037 9
 1038 ]
 1039 ]
 1040 ]
 1041 },
 1042 {
 1043 "actions": [
 1044 [
 1045 144,
 1046 [
 1047 145
 1048 ],
 1049 [
 1050 146,
 1051 9
 1052 ]
 1053 ]
 1054 ]
 1055 },
 1056 {
 1057 "actions": [
 1058 [
 1059 147,
 1060 [
 1061 39,
 1062 5,
 1063 6,
 1064 35
 1065 ],
 1066 [
 1067 148,
 1068 9
 1069 ]
 1070 ],
 1071 [
 1072 147,
 1073 [
 1074 41,
 1075 7,
 1076 8,
 1077 37
 1078 ],
 1079 [
 1080 149,
 1081 9
 1082 ]
 1083 ],
 1084 [
 1085 147,
 1086 [
 1087 9,
 1088 10,
 1089 35
 1090 ],
 1091 [
 1092 150,
 1093 9
 1094 ]
 1095 ],
 1096 [
 1097 147,
 1098 [
 1099 11,
 1100 12,
 1101 37
 1102 ],
 1103 [
 1104 151,
 1105 9
 1106 ]
 1107 ]
 1108 ]
 1109 },
 1110 {
 1111 "actions": [
 1112 [
 1113 152,
 1114 [
 1115 39,
 1116 3,
 1117 4
 1118 ],
 1119 [
 1120 153,
 1121 9
 1122 ]
 1123 ],
 1124 [
 1125 152,
 1126 [
 1127 5,
 1128 6
 1129 ],
 1130 [
 1131 154,
 1132 9
 1133 ]
 1134 ]
 1135 ]
 1136 },
 1137 {
 1138 "actions": [
 1139 [
 1140 155,
 1141 [
 1142 73
 1143 ],
 1144 [
 1145 156,
 1146 9
 1147 ]
 1148 ]
 1149 ]
 1150 },
 1151 {
 1152 "actions": [
 1153 [
 1154 157,
 1155 [
 1156 1
 1157 ],
 1158 [
 1159 158,
 1160 9
 1161 ]
 1162 ]
 1163 ]
 1164 },
 1165 {
 1166 "actions": [
 1167 [
 1168 159,
 1169 [
 1170 73
 1171 ],
 1172 [
 1173 160,
 1174 9
 1175 ]
 1176 ]
 1177 ]
 1178 },
 1179 {
 1180 "actions": [
 1181 [
 1182 161,
 1183 [
 1184 1,
 1185 2
 1186 ],
 1187 [
 1188 162,
 1189 9
 1190 ]
 1191 ]
 1192 ]
 1193 },
 1194 {
 1195 "actions": [
 1196 [
 1197 163,
 1198 [
 1199 1
 1200 ],
 1201 [
 1202 164,
 1203 9
 1204 ]
 1205 ]
 1206 ]
 1207 },
 1208 {
 1209 "actions": [
 1210 [
 1211 165,
 1212 [
 1213 73
 1214 ],
 1215 [
 1216 166,
 1217 9
 1218 ]
 1219 ]
 1220 ]
 1221 },
 1222 {
 1223 "actions": [
 1224 [
 1225 167,
 1226 [
 1227 1
 1228 ],
 1229 [
 1230 168,
 1231 9
 1232 ]
 1233 ]
 1234 ]
 1235 },
 1236 {
 1237 "actions": [
 1238 [
 1239 169,
 1240 [
 1241 1,
 1242 2
 1243 ],
 1244 [
 1245 170,
 1246 9
 1247 ]
 1248 ]
 1249 ]
 1250 },
 1251 {
 1252 "actions": [
 1253 [
 1254 171,
 1255 [
 1256 48,
 1257 5,
 1258 6
 1259 ],
 1260 [
 1261 172,
 1262 9
 1263 ]
 1264 ],
 1265 [
 1266 171,
 1267 [
 1268 173,
 1269 7,
 1270 8,
 1271 9,
 1272 10,
 1273 11,
 1274 12
 1275 ],
 1276 [
 1277 174,
 1278 9
 1279 ]
 1280 ]
 1281 ]
 1282 },
 1283 {
 1284 "actions": [
 1285 [
 1286 175,
 1287 [
 1288 1,
 1289 2,
 1290 3,
 1291 4
 1292 ],
 1293 [
 1294 176,
 1295 9
 1296 ]
 1297 ]
 1298 ]
 1299 },
 1300 {
 1301 "actions": [
 1302 [
 1303 177,
 1304 [
 1305 1,
 1306 2,
 1307 3,
 1308 4
 1309 ],
 1310 [
 1311 178,
 1312 9
 1313 ]
 1314 ]
 1315 ]
 1316 },
 1317 {
 1318 "actions": [
 1319 [
 1320 179,
 1321 [],
 1322 [
 1323 180,
 1324 9
 1325 ]
 1326 ]
 1327 ]
 1328 },
 1329 {
 1330 "actions": [
 1331 [
 1332 181,
 1333 [],
 1334 [
 1335 182,
 1336 9
 1337 ]
 1338 ]
 1339 ]
 1340 },
 1341 {
 1342 "actions": [
 1343 [
 1344 183,
 1345 [
 1346 1
 1347 ],
 1348 [
 1349 184,
 1350 9
 1351 ]
 1352 ]
 1353 ]
 1354 },
 1355 {
 1356 "actions": [
 1357 [
 1358 185,
 1359 [],
 1360 [
 1361 186,
 1362 9
 1363 ]
 1364 ]
 1365 ]
 1366 },
 1367 {
 1368 "actions": [
 1369 [
 1370 187,
 1371 [
 1372 1,
 1373 2
 1374 ],
 1375 [
 1376 188,
 1377 9
 1378 ]
 1379 ]
 1380 ]
 1381 },
 1382 {
 1383 "actions": [
 1384 [
 1385 189,
 1386 [
 1387 null
 1388 ],
 1389 [
 1390 190,
 1391 9
 1392 ]
 1393 ],
 1394 [
 1395 189,
 1396 [
 1397 1
 1398 ],
 1399 [
 1400 191,
 1401 9
 1402 ]
 1403 ]
 1404 ]
 1405 },
 1406 {
 1407 "actions": [
 1408 [
 1409 192,
 1410 [
 1411 193
 1412 ],
 1413 [
 1414 194,
 1415 9
 1416 ]
 1417 ],
 1418 [
 1419 192,
 1420 [
 1421 73
 1422 ],
 1423 [
 1424 195,
 1425 9
 1426 ]
 1427 ]
 1428 ]
 1429 },
 1430 {
 1431 "actions": [
 1432 [
 1433 196,
 1434 [
 1435 58
 1436 ],
 1437 [
 1438 197,
 1439 9
 1440 ]
 1441 ],
 1442 [
 1443 196,
 1444 [
 1445 62,
 1446 1
 1447 ],
 1448 [
 1449 198,
 1450 9
 1451 ]
 1452 ],
 1453 [
 1454 196,
 1455 [
 1456 2,
 1457 1
 1458 ],
 1459 [
 1460 199,
 1461 9
 1462 ]
 1463 ],
 1464 [
 1465 196,
 1466 [
 1467 3,
 1468 4
 1469 ],
 1470 [
 1471 200,
 1472 9
 1473 ]
 1474 ],
 1475 [
 1476 196,
 1477 [
 1478 5,
 1479 6,
 1480 7,
 1481 8
 1482 ],
 1483 [
 1484 201,
 1485 9
 1486 ]
 1487 ],
 1488 [
 1489 196,
 1490 [
 1491 9,
 1492 10,
 1493 11,
 1494 12,
 1495 13
 1496 ],
 1497 [
 1498 202,
 1499 9
 1500 ]
 1501 ]
 1502 ]
 1503 },
 1504 {
 1505 "actions": [
 1506 [
 1507 203,
 1508 [
 1509 193
 1510 ],
 1511 [
 1512 204,
 1513 9
 1514 ]
 1515 ],
 1516 [
 1517 203,
 1518 [
 1519 73
 1520 ],
 1521 [
 1522 205,
 1523 9
 1524 ]
 1525 ]
 1526 ]
 1527 },
 1528 {
 1529 "actions": [
 1530 [
 1531 206,
 1532 [
 1533 [
 1534 1,
 1535 2
 1536 ]
 1537 ],
 1538 [
 1539 207,
 1540 9
 1541 ]
 1542 ]
 1543 ]
 1544 },
 1545 {
 1546 "actions": [
 1547 [
 1548 208,
 1549 [
 1550 193
 1551 ],
 1552 [
 1553 209,
 1554 9
 1555 ]
 1556 ],
 1557 [
 1558 208,
 1559 [
 1560 73
 1561 ],
 1562 [
 1563 210,
 1564 9
 1565 ]
 1566 ]
 1567 ]
 1568 },
 1569 {
 1570 "actions": [
 1571 [
 1572 211,
 1573 [
 1574 null
 1575 ],
 1576 [
 1577 212,
 1578 9
 1579 ]
 1580 ],
 1581 [
 1582 211,
 1583 [
 1584 1
 1585 ],
 1586 [
 1587 213,
 1588 9
 1589 ]
 1590 ]
 1591 ]
 1592 },
 1593 {
 1594 "actions": [
 1595 [
 1596 214,
 1597 [
 1598 null
 1599 ],
 1600 [
 1601 215,
 1602 9
 1603 ]
 1604 ],
 1605 [
 1606 214,
 1607 [
 1608 1
 1609 ],
 1610 [
 1611 216,
 1612 9
 1613 ]
 1614 ]
 1615 ]
 1616 },
 1617 {
 1618 "actions": [
 1619 [
 1620 217,
 1621 [
 1622 1,
 1623 2,
 1624 3,
 1625 218
 1626 ],
 1627 [
 1628 219,
 1629 9
 1630 ]
 1631 ],
 1632 [
 1633 217,
 1634 [
 1635 4,
 1636 5,
 1637 6,
 1638 73,
 1639 7
 1640 ],
 1641 [
 1642 220,
 1643 9
 1644 ]
 1645 ],
 1646 [
 1647 217,
 1648 [
 1649 8,
 1650 9,
 1651 10,
 1652 11,
 1653 1
 1654 ],
 1655 [
 1656 221,
 1657 9
 1658 ]
 1659 ],
 1660 [
 1661 217,
 1662 [
 1663 12,
 1664 13,
 1665 14,
 1666 15,
 1667 16
 1668 ],
 1669 [
 1670 222,
 1671 9
 1672 ]
 1673 ],
 1674 [
 1675 217,
 1676 [
 1677 17,
 1678 18,
 1679 19,
 1680 20,
 1681 21,
 1682 22,
 1683 23
 1684 ],
 1685 [
 1686 223,
 1687 9
 1688 ]
 1689 ],
 1690 [
 1691 217,
 1692 [
 1693 24,
 1694 25,
 1695 26,
 1696 27,
 1697 28,
 1698 29,
 1699 30,
 1700 31
 1701 ],
 1702 [
 1703 224,
 1704 9
 1705 ]
 1706 ]
 1707 ]
 1708 },
 1709 {
 1710 "actions": [
 1711 [
 1712 225,
 1713 [
 1714 58
 1715 ],
 1716 [
 1717 226,
 1718 9
 1719 ]
 1720 ],
 1721 [
 1722 225,
 1723 [
 1724 62,
 1725 1
 1726 ],
 1727 [
 1728 227,
 1729 9
 1730 ]
 1731 ],
 1732 [
 1733 225,
 1734 [
 1735 2,
 1736 1
 1737 ],
 1738 [
 1739 228,
 1740 9
 1741 ]
 1742 ],
 1743 [
 1744 225,
 1745 [
 1746 3,
 1747 4
 1748 ],
 1749 [
 1750 229,
 1751 9
 1752 ]
 1753 ],
 1754 [
 1755 225,
 1756 [
 1757 5,
 1758 6,
 1759 7,
 1760 8
 1761 ],
 1762 [
 1763 230,
 1764 9
 1765 ]
 1766 ],
 1767 [
 1768 225,
 1769 [
 1770 9,
 1771 10,
 1772 11,
 1773 12,
 1774 13
 1775 ],
 1776 [
 1777 231,
 1778 9
 1779 ]
 1780 ]
 1781 ]
 1782 },
 1783 {
 1784 "actions": [
 1785 [
 1786 232,
 1787 [
 1788 1,
 1789 2,
 1790 3,
 1791 4,
 1792 5,
 1793 6
 1794 ],
 1795 [
 1796 233,
 1797 9
 1798 ]
 1799 ],
 1800 [
 1801 232,
 1802 [
 1803 1,
 1804 0,
 1805 0,
 1806 1,
 1807 0,
 1808 0
 1809 ],
 1810 [
 1811 234,
 1812 6,
 1813 235,
 1814 9
 1815 ]
 1816 ],
 1817 [
 1818 232,
 1819 [
 1820 7,
 1821 8,
 1822 9,
 1823 10,
 1824 11,
 1825 12
 1826 ],
 1827 [
 1828 236,
 1829 6,
 1830 237,
 1831 9
 1832 ]
 1833 ]
 1834 ]
 1835 },
 1836 {
 1837 "actions": [
 1838 [
 1839 238,
 1840 [
 1841 1
 1842 ],
 1843 [
 1844 239,
 1845 9
 1846 ]
 1847 ]
 1848 ]
 1849 },
 1850 {
 1851 "actions": [
 1852 [
 1853 240,
 1854 [
 1855 73
 1856 ],
 1857 [
 1858 241,
 1859 9
 1860 ]
 1861 ]
 1862 ]
 1863 },
 1864 {
 1865 "actions": [
 1866 [
 1867 242,
 1868 [
 1869 1
 1870 ],
 1871 [
 1872 243,
 1873 9
 1874 ]
 1875 ]
 1876 ]
 1877 },
 1878 {
 1879 "actions": [
 1880 [
 1881 244,
 1882 [
 1883 1
 1884 ],
 1885 [
 1886 245,
 1887 9
 1888 ]
 1889 ]
 1890 ]
 1891 },
 1892 {
 1893 "actions": [
 1894 [
 1895 246,
 1896 [],
 1897 [
 1898 247,
 1899 9
 1900 ]
 1901 ],
 1902 [
 1903 246,
 1904 [
 1905 39
 1906 ],
 1907 [
 1908 248,
 1909 9
 1910 ]
 1911 ]
 1912 ]
 1913 },
 1914 {
 1915 "actions": [
 1916 [
 1917 249,
 1918 [
 1919 1,
 1920 2,
 1921 3,
 1922 4
 1923 ],
 1924 [
 1925 250,
 1926 9
 1927 ]
 1928 ]
 1929 ]
 1930 },
 1931 {
 1932 "actions": [
 1933 [
 1934 251,
 1935 [
 1936 73
 1937 ],
 1938 [
 1939 252,
 1940 9
 1941 ]
 1942 ],
 1943 [
 1944 251,
 1945 [
 1946 116
 1947 ],
 1948 [
 1949 253,
 1950 9
 1951 ]
 1952 ],
 1953 [
 1954 251,
 1955 [
 1956 119
 1957 ],
 1958 [
 1959 254,
 1960 9
 1961 ]
 1962 ],
 1963 [
 1964 251,
 1965 [
 1966 124
 1967 ],
 1968 [
 1969 255,
 1970 9
 1971 ]
 1972 ]
 1973 ]
 1974 },
 1975 {
 1976 "actions": [
 1977 [
 1978 256,
 1979 [
 1980 58,
 1981 1,
 1982 2
 1983 ],
 1984 [
 1985 257,
 1986 9
 1987 ]
 1988 ],
 1989 [
 1990 256,
 1991 [
 1992 62,
 1993 3,
 1994 4,
 1995 5
 1996 ],
 1997 [
 1998 258,
 1999 9
 2000 ]
 2001 ]
 2002 ]
 2003 },
 2004 {
 2005 "actions": [
 2006 [
 2007 259,
 2008 [
 2009 73
 2010 ],
 2011 [
 2012 260,
 2013 9
 2014 ]
 2015 ]
 2016 ]
 2017 },
 2018 {
 2019 "actions": [
 2020 [
 2021 261,
 2022 [
 2023 73
 2024 ],
 2025 [
 2026 262,
 2027 9
 2028 ]
 2029 ]
 2030 ]
 2031 },
 2032 {
 2033 "actions": [
 2034 [
 2035 263,
 2036 [
 2037 1,
 2038 2,
 2039 3,
 2040 4,
 2041 5,
 2042 6
 2043 ],
 2044 [
 2045 264,
 2046 9
 2047 ]
 2048 ]
 2049 ]
 2050 },
 2051 {
 2052 "actions": [
 2053 [
 2054 265,
 2055 [
 2056 1,
 2057 2
 2058 ],
 2059 [
 2060 266,
 2061 9
 2062 ]
 2063 ]
 2064 ]
 2065 },
 2066 {
 2067 "actions": [
 2068 [
 2069 267,
 2070 [
 2071 1,
 2072 2,
 2073 3,
 2074 4
 2075 ],
 2076 [
 2077 268,
 2078 6,
 2079 269,
 2080 9
 2081 ]
 2082 ]
 2083 ]
 2084 },
 2085 {
 2086 "actions": [
 2087 [
 2088 270,
 2089 [
 2090 1
 2091 ],
 2092 [
 2093 271,
 2094 9
 2095 ]
 2096 ]
 2097 ]
 2098 },
 2099 {
 2100 "actions": [
 2101 [
 2102 272,
 2103 [
 2104 [
 2105 1,
 2106 2
 2107 ]
 2108 ],
 2109 [
 2110 273,
 2111 9
 2112 ]
 2113 ]
 2114 ]
 2115 },
 2116 {
 2117 "actions": [
 2118 [
 2119 274,
 2120 [
 2121 1
 2122 ],
 2123 [
 2124 275,
 2125 9
 2126 ]
 2127 ]
 2128 ]
 2129 },
 2130 {
 2131 "actions": [
 2132 [
 2133 276,
 2134 [
 2135 48,
 2136 5,
 2137 6
 2138 ],
 2139 [
 2140 277,
 2141 9
 2142 ]
 2143 ],
 2144 [
 2145 276,
 2146 [
 2147 173,
 2148 7,
 2149 8,
 2150 9,
 2151 10,
 2152 11,
 2153 12
 2154 ],
 2155 [
 2156 278,
 2157 9
 2158 ]
 2159 ]
 2160 ]
 2161 }
 2162 ],
 2163 "data": [
 2164 "#000000",
 2165 "arc",
 2166 "",
 2167 "/LayoutTests/inspector/canvas/recording-2d.html",
 2168 [
 2169 2,
 2170 3,
 2171 56,
 2172 28
 2173 ],
 2174 "e",
 2175 [
 2176 5,
 2177 3,
 2178 44,
 2179 12
 2180 ],
 2181 [
 2182 2,
 2183 3,
 2184 56,
 2185 14
 2186 ],
 2187 "f",
 2188 [
 2189 8,
 2190 3,
 2191 348,
 2192 24
 2193 ],
 2194 "performActions",
 2195 [
 2196 10,
 2197 3,
 2198 352,
 2199 6
 2200 ],
 2201 "global code",
 2202 [
 2203 12,
 2204 2,
 2205 1,
 2206 15
 2207 ],
 2208 "evaluateWithScopeExtension",
 2209 "[native code]",
 2210 [
 2211 14,
 2212 15,
 2213 0,
 2214 0
 2215 ],
 2216 "_evaluateOn",
 2217 [
 2218 17,
 2219 2,
 2220 128,
 2221 29
 2222 ],
 2223 "_evaluateAndWrap",
 2224 [
 2225 19,
 2226 2,
 2227 122,
 2228 108
 2229 ],
 2230 [
 2231 2,
 2232 3,
 2233 57,
 2234 28
 2235 ],
 2236 [
 2237 2,
 2238 3,
 2239 57,
 2240 14
 2241 ],
 2242 "arcTo",
 2243 [
 2244 2,
 2245 3,
 2246 60,
 2247 30
 2248 ],
 2249 [
 2250 2,
 2251 3,
 2252 60,
 2253 14
 2254 ],
 2255 "beginPath",
 2256 [
 2257 2,
 2258 3,
 2259 63,
 2260 26
 2261 ],
 2262 "bezierCurveTo",
 2263 [
 2264 2,
 2265 3,
 2266 66,
 2267 30
 2268 ],
 2269 "clearRect",
 2270 [
 2271 2,
 2272 3,
 2273 69,
 2274 26
 2275 ],
 2276 "clearShadow",
 2277 [
 2278 2,
 2279 3,
 2280 72,
 2281 28
 2282 ],
 2283 "clip",
 2284 "nonzero",
 2285 [
 2286 2,
 2287 3,
 2288 75,
 2289 21
 2290 ],
 2291 "evenodd",
 2292 [
 2293 2,
 2294 3,
 2295 76,
 2296 21
 2297 ],
 2298 "M1 2",
 2299 [
 2300 2,
 2301 3,
 2302 77,
 2303 21
 2304 ],
 2305 "M3 4",
 2306 [
 2307 2,
 2308 3,
 2309 78,
 2310 21
 2311 ],
 2312 "closePath",
 2313 [
 2314 2,
 2315 3,
 2316 81,
 2317 26
 2318 ],
 2319 "commit",
 2320 [
 2321 2,
 2322 3,
 2323 84,
 2324 23
 2325 ],
 2326 "createImageData",
 2327 [
 2328 [
 2329 0,
 2330 0,
 2331 0,
 2332 0,
 2333 0,
 2334 0,
 2335 0,
 2336 0,
 2337 0,
 2338 0,
 2339 0,
 2340 0,
 2341 0,
 2342 0,
 2343 0,
 2344 0
 2345 ],
 2346 1,
 2347 4
 2348 ],
 2349 [
 2350 2,
 2351 3,
 2352 87,
 2353 40
 2354 ],
 2355 [
 2356 2,
 2357 3,
 2358 87,
 2359 14
 2360 ],
 2361 [
 2362 2,
 2363 3,
 2364 88,
 2365 40
 2366 ],
 2367 [
 2368 2,
 2369 3,
 2370 88,
 2371 14
 2372 ],
 2373 "createLinearGradient",
 2374 [
 2375 2,
 2376 3,
 2377 91,
 2378 45
 2379 ],
 2380 [
 2381 2,
 2382 3,
 2383 91,
 2384 14
 2385 ],
 2386 "createPattern",
 2387 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABNJREFUCB1j/M/AAEQMDEwgAgQAHxcCAmtAm/sAAAAASUVORK5CYII=",
 2388 "testA",
 2389 [
 2390 2,
 2391 3,
 2392 94,
 2393 38
 2394 ],
 2395 [
 2396 2,
 2397 3,
 2398 94,
 2399 14
 2400 ],
 2401 "data:,",
 2402 "testB",
 2403 [
 2404 2,
 2405 3,
 2406 95,
 2407 38
 2408 ],
 2409 [
 2410 2,
 2411 3,
 2412 95,
 2413 14
 2414 ],
 2415 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAAAtJREFUCB1jYEAHAAASAAGAFMrMAAAAAElFTkSuQmCC",
 2416 "testC",
 2417 [
 2418 2,
 2419 3,
 2420 96,
 2421 38
 2422 ],
 2423 [
 2424 2,
 2425 3,
 2426 96,
 2427 14
 2428 ],
 2429 "createRadialGradient",
 2430 [
 2431 2,
 2432 3,
 2433 99,
 2434 45
 2435 ],
 2436 [
 2437 2,
 2438 3,
 2439 99,
 2440 14
 2441 ],
 2442 "direction",
 2443 "test",
 2444 [
 2445 2,
 2446 3,
 2447 102,
 2448 16
 2449 ],
 2450 "drawFocusIfNeeded",
 2451 "Element",
 2452 [
 2453 2,
 2454 3,
 2455 105,
 2456 34
 2457 ],
 2458 [
 2459 2,
 2460 3,
 2461 106,
 2462 34
 2463 ],
 2464 "drawImage",
 2465 [
 2466 2,
 2467 3,
 2468 109,
 2469 34
 2470 ],
 2471 [
 2472 2,
 2473 3,
 2474 109,
 2475 14
 2476 ],
 2477 [
 2478 2,
 2479 3,
 2480 110,
 2481 34
 2482 ],
 2483 [
 2484 2,
 2485 3,
 2486 110,
 2487 14
 2488 ],
 2489 [
 2490 2,
 2491 3,
 2492 111,
 2493 34
 2494 ],
 2495 [
 2496 2,
 2497 3,
 2498 111,
 2499 14
 2500 ],
 2501 [
 2502 2,
 2503 3,
 2504 112,
 2505 34
 2506 ],
 2507 [
 2508 2,
 2509 3,
 2510 112,
 2511 14
 2512 ],
 2513 [
 2514 2,
 2515 3,
 2516 113,
 2517 34
 2518 ],
 2519 [
 2520 2,
 2521 3,
 2522 113,
 2523 14
 2524 ],
 2525 [
 2526 2,
 2527 3,
 2528 114,
 2529 34
 2530 ],
 2531 [
 2532 2,
 2533 3,
 2534 114,
 2535 14
 2536 ],
 2537 [
 2538 2,
 2539 3,
 2540 115,
 2541 34
 2542 ],
 2543 [
 2544 2,
 2545 3,
 2546 115,
 2547 14
 2548 ],
 2549 [
 2550 2,
 2551 3,
 2552 116,
 2553 34
 2554 ],
 2555 [
 2556 2,
 2557 3,
 2558 116,
 2559 14
 2560 ],
 2561 [
 2562 2,
 2563 3,
 2564 117,
 2565 34
 2566 ],
 2567 [
 2568 2,
 2569 3,
 2570 117,
 2571 14
 2572 ],
 2573 "drawImageFromRect",
 2574 [
 2575 2,
 2576 3,
 2577 120,
 2578 34
 2579 ],
 2580 [
 2581 2,
 2582 3,
 2583 121,
 2584 34
 2585 ],
 2586 "ellipse",
 2587 [
 2588 2,
 2589 3,
 2590 124,
 2591 32
 2592 ],
 2593 [
 2594 2,
 2595 3,
 2596 124,
 2597 14
 2598 ],
 2599 [
 2600 2,
 2601 3,
 2602 125,
 2603 32
 2604 ],
 2605 [
 2606 2,
 2607 3,
 2608 125,
 2609 14
 2610 ],
 2611 "fill",
 2612 [
 2613 2,
 2614 3,
 2615 128,
 2616 21
 2617 ],
 2618 [
 2619 2,
 2620 3,
 2621 129,
 2622 21
 2623 ],
 2624 [
 2625 2,
 2626 3,
 2627 130,
 2628 21
 2629 ],
 2630 [
 2631 2,
 2632 3,
 2633 131,
 2634 21
 2635 ],
 2636 "fillRect",
 2637 [
 2638 2,
 2639 3,
 2640 134,
 2641 25
 2642 ],
 2643 "fillStyle",
 2644 [
 2645 2,
 2646 3,
 2647 137,
 2648 16
 2649 ],
 2650 "linear-gradient",
 2651 [
 2652 115,
 2653 [
 2654 1,
 2655 2,
 2656 3,
 2657 4
 2658 ],
 2659 []
 2660 ],
 2661 [
 2662 2,
 2663 3,
 2664 138,
 2665 16
 2666 ],
 2667 "radial-gradient",
 2668 [
 2669 118,
 2670 [
 2671 1,
 2672 2,
 2673 3,
 2674 4,
 2675 5,
 2676 6
 2677 ],
 2678 []
 2679 ],
 2680 [
 2681 2,
 2682 3,
 2683 139,
 2684 16
 2685 ],
 2686 "pattern",
 2687 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABNJREFUCB1j/M/AAEQMDEwgAgQAHxcCAmtAm/sAAAAASUVORK5CYII=",
 2688 "no-repeat",
 2689 [
 2690 121,
 2691 122,
 2692 123
 2693 ],
 2694 [
 2695 2,
 2696 3,
 2697 140,
 2698 16
 2699 ],
 2700 "fillText",
 2701 [
 2702 2,
 2703 3,
 2704 143,
 2705 25
 2706 ],
 2707 [
 2708 2,
 2709 3,
 2710 144,
 2711 25
 2712 ],
 2713 "font",
 2714 [
 2715 2,
 2716 3,
 2717 147,
 2718 16
 2719 ],
 2720 "getImageData",
 2721 [
 2722 2,
 2723 3,
 2724 150,
 2725 37
 2726 ],
 2727 [
 2728 2,
 2729 3,
 2730 150,
 2731 14
 2732 ],
 2733 "getLineDash",
 2734 [
 2735 2,
 2736 3,
 2737 153,
 2738 28
 2739 ],
 2740 "getTransform",
 2741 [
 2742 2,
 2743 3,
 2744 156,
 2745 29
 2746 ],
 2747 "globalAlpha",
 2748 [
 2749 2,
 2750 3,
 2751 159,
 2752 16
 2753 ],
 2754 "globalCompositeOperation",
 2755 [
 2756 2,
 2757 3,
 2758 162,
 2759 16
 2760 ],
 2761 "imageSmoothingEnabled",
 2762 [
 2763 2,
 2764 3,
 2765 165,
 2766 16
 2767 ],
 2768 "imageSmoothingQuality",
 2769 "low",
 2770 [
 2771 2,
 2772 3,
 2773 168,
 2774 16
 2775 ],
 2776 "isPointInPath",
 2777 [
 2778 2,
 2779 3,
 2780 171,
 2781 30
 2782 ],
 2783 [
 2784 2,
 2785 3,
 2786 172,
 2787 30
 2788 ],
 2789 [
 2790 2,
 2791 3,
 2792 173,
 2793 30
 2794 ],
 2795 [
 2796 2,
 2797 3,
 2798 174,
 2799 30
 2800 ],
 2801 "isPointInStroke",
 2802 [
 2803 2,
 2804 3,
 2805 177,
 2806 32
 2807 ],
 2808 [
 2809 2,
 2810 3,
 2811 178,
 2812 32
 2813 ],
 2814 "lineCap",
 2815 [
 2816 2,
 2817 3,
 2818 181,
 2819 16
 2820 ],
 2821 "lineDashOffset",
 2822 [
 2823 2,
 2824 3,
 2825 184,
 2826 16
 2827 ],
 2828 "lineJoin",
 2829 [
 2830 2,
 2831 3,
 2832 187,
 2833 16
 2834 ],
 2835 "lineTo",
 2836 [
 2837 2,
 2838 3,
 2839 190,
 2840 23
 2841 ],
 2842 "lineWidth",
 2843 [
 2844 2,
 2845 3,
 2846 193,
 2847 16
 2848 ],
 2849 "measureText",
 2850 [
 2851 2,
 2852 3,
 2853 196,
 2854 28
 2855 ],
 2856 "miterLimit",
 2857 [
 2858 2,
 2859 3,
 2860 199,
 2861 16
 2862 ],
 2863 "moveTo",
 2864 [
 2865 2,
 2866 3,
 2867 202,
 2868 23
 2869 ],
 2870 "putImageData",
 2871 [
 2872 2,
 2873 3,
 2874 205,
 2875 29
 2876 ],
 2877 [
 2878 [
 2879 0,
 2880 0,
 2881 0,
 2882 0,
 2883 0,
 2884 0,
 2885 0,
 2886 0,
 2887 0,
 2888 0,
 2889 0,
 2890 0,
 2891 0,
 2892 0,
 2893 0,
 2894 0,
 2895 0,
 2896 0,
 2897 0,
 2898 0,
 2899 0,
 2900 0,
 2901 0,
 2902 0
 2903 ],
 2904 2,
 2905 3
 2906 ],
 2907 [
 2908 2,
 2909 3,
 2910 206,
 2911 29
 2912 ],
 2913 "quadraticCurveTo",
 2914 [
 2915 2,
 2916 3,
 2917 209,
 2918 33
 2919 ],
 2920 "rect",
 2921 [
 2922 2,
 2923 3,
 2924 212,
 2925 21
 2926 ],
 2927 "resetTransform",
 2928 [
 2929 2,
 2930 3,
 2931 215,
 2932 31
 2933 ],
 2934 "restore",
 2935 [
 2936 2,
 2937 3,
 2938 218,
 2939 24
 2940 ],
 2941 "rotate",
 2942 [
 2943 2,
 2944 3,
 2945 221,
 2946 23
 2947 ],
 2948 "save",
 2949 [
 2950 2,
 2951 3,
 2952 224,
 2953 21
 2954 ],
 2955 "scale",
 2956 [
 2957 2,
 2958 3,
 2959 227,
 2960 22
 2961 ],
 2962 "setAlpha",
 2963 [
 2964 2,
 2965 3,
 2966 230,
 2967 25
 2968 ],
 2969 [
 2970 2,
 2971 3,
 2972 231,
 2973 25
 2974 ],
 2975 "setCompositeOperation",
 2976 "undefined",
 2977 [
 2978 2,
 2979 3,
 2980 234,
 2981 38
 2982 ],
 2983 [
 2984 2,
 2985 3,
 2986 235,
 2987 38
 2988 ],
 2989 "setFillColor",
 2990 [
 2991 2,
 2992 3,
 2993 238,
 2994 29
 2995 ],
 2996 [
 2997 2,
 2998 3,
 2999 239,
 3000 29
 3001 ],
 3002 [
 3003 2,
 3004 3,
 3005 240,
 3006 29
 3007 ],
 3008 [
 3009 2,
 3010 3,
 3011 241,
 3012 29
 3013 ],
 3014 [
 3015 2,
 3016 3,
 3017 242,
 3018 29
 3019 ],
 3020 [
 3021 2,
 3022 3,
 3023 243,
 3024 29
 3025 ],
 3026 "setLineCap",
 3027 [
 3028 2,
 3029 3,
 3030 246,
 3031 27
 3032 ],
 3033 [
 3034 2,
 3035 3,
 3036 247,
 3037 27
 3038 ],
 3039 "setLineDash",
 3040 [
 3041 2,
 3042 3,
 3043 250,
 3044 28
 3045 ],
 3046 "setLineJoin",
 3047 [
 3048 2,
 3049 3,
 3050 253,
 3051 28
 3052 ],
 3053 [
 3054 2,
 3055 3,
 3056 254,
 3057 28
 3058 ],
 3059 "setLineWidth",
 3060 [
 3061 2,
 3062 3,
 3063 257,
 3064 29
 3065 ],
 3066 [
 3067 2,
 3068 3,
 3069 258,
 3070 29
 3071 ],
 3072 "setMiterLimit",
 3073 [
 3074 2,
 3075 3,
 3076 261,
 3077 30
 3078 ],
 3079 [
 3080 2,
 3081 3,
 3082 262,
 3083 30
 3084 ],
 3085 "setShadow",
 3086 "",
 3087 [
 3088 2,
 3089 3,
 3090 265,
 3091 26
 3092 ],
 3093 [
 3094 2,
 3095 3,
 3096 266,
 3097 26
 3098 ],
 3099 [
 3100 2,
 3101 3,
 3102 267,
 3103 26
 3104 ],
 3105 [
 3106 2,
 3107 3,
 3108 268,
 3109 26
 3110 ],
 3111 [
 3112 2,
 3113 3,
 3114 269,
 3115 26
 3116 ],
 3117 [
 3118 2,
 3119 3,
 3120 270,
 3121 26
 3122 ],
 3123 "setStrokeColor",
 3124 [
 3125 2,
 3126 3,
 3127 273,
 3128 31
 3129 ],
 3130 [
 3131 2,
 3132 3,
 3133 274,
 3134 31
 3135 ],
 3136 [
 3137 2,
 3138 3,
 3139 275,
 3140 31
 3141 ],
 3142 [
 3143 2,
 3144 3,
 3145 276,
 3146 31
 3147 ],
 3148 [
 3149 2,
 3150 3,
 3151 277,
 3152 31
 3153 ],
 3154 [
 3155 2,
 3156 3,
 3157 278,
 3158 31
 3159 ],
 3160 "setTransform",
 3161 [
 3162 2,
 3163 3,
 3164 281,
 3165 29
 3166 ],
 3167 [
 3168 2,
 3169 3,
 3170 282,
 3171 37
 3172 ],
 3173 [
 3174 2,
 3175 3,
 3176 282,
 3177 14
 3178 ],
 3179 [
 3180 2,
 3181 3,
 3182 283,
 3183 37
 3184 ],
 3185 [
 3186 2,
 3187 3,
 3188 283,
 3189 14
 3190 ],
 3191 "shadowBlur",
 3192 [
 3193 2,
 3194 3,
 3195 286,
 3196 16
 3197 ],
 3198 "shadowColor",
 3199 [
 3200 2,
 3201 3,
 3202 289,
 3203 16
 3204 ],
 3205 "shadowOffsetX",
 3206 [
 3207 2,
 3208 3,
 3209 292,
 3210 16
 3211 ],
 3212 "shadowOffsetY",
 3213 [
 3214 2,
 3215 3,
 3216 295,
 3217 16
 3218 ],
 3219 "stroke",
 3220 [
 3221 2,
 3222 3,
 3223 298,
 3224 23
 3225 ],
 3226 [
 3227 2,
 3228 3,
 3229 299,
 3230 23
 3231 ],
 3232 "strokeRect",
 3233 [
 3234 2,
 3235 3,
 3236 302,
 3237 27
 3238 ],
 3239 "strokeStyle",
 3240 [
 3241 2,
 3242 3,
 3243 305,
 3244 16
 3245 ],
 3246 [
 3247 2,
 3248 3,
 3249 306,
 3250 16
 3251 ],
 3252 [
 3253 2,
 3254 3,
 3255 307,
 3256 16
 3257 ],
 3258 [
 3259 2,
 3260 3,
 3261 308,
 3262 16
 3263 ],
 3264 "strokeText",
 3265 [
 3266 2,
 3267 3,
 3268 311,
 3269 27
 3270 ],
 3271 [
 3272 2,
 3273 3,
 3274 312,
 3275 27
 3276 ],
 3277 "textAlign",
 3278 [
 3279 2,
 3280 3,
 3281 315,
 3282 16
 3283 ],
 3284 "textBaseline",
 3285 [
 3286 2,
 3287 3,
 3288 318,
 3289 16
 3290 ],
 3291 "transform",
 3292 [
 3293 2,
 3294 3,
 3295 321,
 3296 26
 3297 ],
 3298 "translate",
 3299 [
 3300 2,
 3301 3,
 3302 324,
 3303 26
 3304 ],
 3305 "webkitGetImageDataHD",
 3306 [
 3307 2,
 3308 3,
 3309 327,
 3310 45
 3311 ],
 3312 [
 3313 2,
 3314 3,
 3315 327,
 3316 14
 3317 ],
 3318 "webkitImageSmoothingEnabled",
 3319 [
 3320 2,
 3321 3,
 3322 330,
 3323 16
 3324 ],
 3325 "webkitLineDash",
 3326 [
 3327 2,
 3328 3,
 3329 333,
 3330 16
 3331 ],
 3332 "webkitLineDashOffset",
 3333 [
 3334 2,
 3335 3,
 3336 336,
 3337 16
 3338 ],
 3339 "webkitPutImageDataHD",
 3340 [
 3341 2,
 3342 3,
 3343 339,
 3344 37
 3345 ],
 3346 [
 3347 2,
 3348 3,
 3349 340,
 3350 37
 3351 ]
 3352 ]
 3353}
 3354
 3355-- Running test case: Canvas.recording2D.memoryLimit
 3356{
 3357 "version": 1,
 3358 "type": "canvas-2d",
 3359 "initialState": {
 3360 "attributes": {
 3361 "width": 2,
 3362 "height": 2,
 3363 "setTransform": [
 3364 25,
 3365 28,
 3366 57,
 3367 64,
 3368 239,
 3369 268
 3370 ],
 3371 "globalAlpha": 1,
 3372 "globalCompositeOperation": "source-over",
 3373 "lineWidth": 1,
 3374 "lineCap": "butt",
 3375 "lineJoin": "miter",
 3376 "miterLimit": 1,
 3377 "shadowOffsetX": 1,
 3378 "shadowOffsetY": 1,
 3379 "shadowBlur": 1,
 3380 "shadowColor": "#ffffff",
 3381 "setLineDash": [
 3382 [
 3383 1,
 3384 2
 3385 ]
 3386 ],
 3387 "lineDashOffset": 1,
 3388 "font": "10px sans-serif",
 3389 "textAlign": "start",
 3390 "textBaseline": "alphabetic",
 3391 "direction": "ltr",
 3392 "strokeStyle": 3,
 3393 "fillStyle": 3,
 3394 "imageSmoothingEnabled": true,
 3395 "imageSmoothingQuality": "low"
 3396 },
 3397 "content": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAAAtJREFUCB1jYEAHAAASAAGAFMrMAAAAAElFTkSuQmCC"
 3398 },
 3399 "frames": [
 3400 {
 3401 "actions": [
 3402 [
 3403 5,
 3404 [
 3405 1,
 3406 2,
 3407 3,
 3408 4,
 3409 5,
 3410 0
 3411 ],
 3412 [
 3413 8,
 3414 10,
 3415 11,
 3416 13,
 3417 15,
 3418 17,
 3419 20,
 3420 22,
 3421 24
 3422 ]
 3423 ]
 3424 ],
 3425 "incomplete": true
 3426 }
 3427 ],
 3428 "data": [
 3429 "pattern",
 3430 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABNJREFUCB1j/M/AAEQMDEwgAgQAHxcCAmtAm/sAAAAASUVORK5CYII=",
 3431 "no-repeat",
 3432 [
 3433 0,
 3434 1,
 3435 2
 3436 ],
 3437 "#ffffff",
 3438 "arc",
 3439 "",
 3440 "/LayoutTests/inspector/canvas/recording-2d.html",
 3441 [
 3442 6,
 3443 7,
 3444 56,
 3445 28
 3446 ],
 3447 "e",
 3448 [
 3449 9,
 3450 7,
 3451 44,
 3452 12
 3453 ],
 3454 [
 3455 6,
 3456 7,
 3457 56,
 3458 14
 3459 ],
 3460 "f",
 3461 [
 3462 12,
 3463 7,
 3464 348,
 3465 24
 3466 ],
 3467 "performActions",
 3468 [
 3469 14,
 3470 7,
 3471 352,
 3472 6
 3473 ],
 3474 "global code",
 3475 [
 3476 16,
 3477 6,
 3478 1,
 3479 15
 3480 ],
 3481 "evaluateWithScopeExtension",
 3482 "[native code]",
 3483 [
 3484 18,
 3485 19,
 3486 0,
 3487 0
 3488 ],
 3489 "_evaluateOn",
 3490 [
 3491 21,
 3492 6,
 3493 128,
 3494 29
 3495 ],
 3496 "_evaluateAndWrap",
 3497 [
 3498 23,
 3499 6,
 3500 122,
 3501 108
 3502 ]
 3503 ]
 3504}
 3505
 3506-- Running test case: Canvas.requestRecording.InvalidCanvasId
 3507PASS: Should produce an error.
 3508Error: No canvas for given identifier.
 3509
 3510-- Running test case: Canvas.cancelRecording.InvalidCanvasId
 3511PASS: Should produce an error.
 3512Error: No canvas for given identifier.
 3513

LayoutTests/inspector/canvas/recording-2d.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
 5<script>
 6let ctx = null;
 7
 8// 2x2 red square
 9let image = document.createElement("img");
 10image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABNJREFUCB1j/M/AAEQMDEwgAgQAHxcCAmtAm/sAAAAASUVORK5CYII=";
 11
 12// Invalid video
 13let video = document.createElement("video");
 14
 15// Blank canvas
 16let canvas = document.createElement("canvas");
 17canvas.width = 2;
 18canvas.height = 2;
 19
 20let linearGradient = null;
 21
 22let radialGradient = null;
 23
 24let pattern = null;
 25
 26let path12 = new Path2D("M 1 2");
 27let path34 = new Path2D("M 3 4");
 28
 29let imageData14 = new ImageData(1, 4);
 30let imageData23 = new ImageData(2, 3);
 31
 32function load() {
 33 ctx = canvas.getContext("2d");
 34 linearGradient = ctx.createLinearGradient(1, 2, 3, 4);
 35 radialGradient = ctx.createRadialGradient(1, 2, 3, 4, 5, 6);
 36 pattern = ctx.createPattern(image, "no-repeat");
 37
 38 document.body.appendChild(canvas);
 39
 40 runTest();
 41}
 42
 43function e(f){
 44 try { f(); } catch(e) { }
 45}
 46
 47let animationFrameID = false;
 48
 49function cancelActions() {
 50 window.cancelAnimationFrame(animationFrameID);
 51}
 52
 53function performActions() {
 54 let frames = [
 55 () => {
 56 e(() => ctx.arc(1, 2, 3, 4, 5));
 57 e(() => ctx.arc(6, 7, 8, 9, 10, true));
 58 },
 59 () => {
 60 e(() => ctx.arcTo(1, 2, 3, 4, 5));
 61 },
 62 () => {
 63 ctx.beginPath();
 64 },
 65 () => {
 66 ctx.bezierCurveTo(1, 2, 3, 4, 5, 6);
 67 },
 68 () => {
 69 ctx.canvas;
 70 },
 71 () => {
 72 ctx.clearRect(1, 2, 3, 4);
 73 },
 74 () => {
 75 ctx.clearShadow();
 76 },
 77 () => {
 78 ctx.clip();
 79 ctx.clip("evenodd");
 80 ctx.clip(path12);
 81 ctx.clip(path34, "evenodd");
 82 },
 83 () => {
 84 ctx.closePath();
 85 },
 86 () => {
 87 ctx.commit();
 88 },
 89 () => {
 90 e(() => ctx.createImageData(imageData14));
 91 e(() => ctx.createImageData(2, 3));
 92 },
 93 () => {
 94 e(() => ctx.createLinearGradient(1, 2, 3, 4));
 95 },
 96 () => {
 97 e(() => ctx.createPattern(image, "testA"));
 98 e(() => ctx.createPattern(video, "testB"));
 99 e(() => ctx.createPattern(canvas, "testC"));
 100 },
 101 () => {
 102 e(() => ctx.createRadialGradient(1, 2, 3, 4, 5, 6));
 103 },
 104 () => {
 105 ctx.direction;
 106 ctx.direction = "test";
 107 },
 108 () => {
 109 ctx.drawFocusIfNeeded(document.body);
 110 ctx.drawFocusIfNeeded(path12, document.body);
 111 },
 112 () => {
 113 e(() => ctx.drawImage(image, 1, 2));
 114 e(() => ctx.drawImage(video, 3, 4));
 115 e(() => ctx.drawImage(canvas, 5, 6));
 116 e(() => ctx.drawImage(image, 7, 8, 9, 10));
 117 e(() => ctx.drawImage(video, 11, 12, 13, 14));
 118 e(() => ctx.drawImage(canvas, 15, 16, 17, 18));
 119 e(() => ctx.drawImage(image, 19, 20, 21, 22, 23, 24, 25, 26));
 120 e(() => ctx.drawImage(video, 27, 28, 29, 30, 31, 32, 33, 34));
 121 e(() => ctx.drawImage(canvas, 35, 36, 37, 38, 39, 40, 41 ,42));
 122 },
 123 () => {
 124 ctx.drawImageFromRect(image, 1, 2, 3, 4, 5, 6, 7, 8)
 125 ctx.drawImageFromRect(image, 9, 10, 11, 12, 13, 14, 15, 16, "test");
 126 },
 127 () => {
 128 e(() => ctx.ellipse(1, 2, 3, 4, 5, 6, 7));
 129 e(() => ctx.ellipse(8, 9, 10, 11, 12, 13, 14, true));
 130 },
 131 () => {
 132 ctx.fill();
 133 ctx.fill("evenodd");
 134 ctx.fill(path12);
 135 ctx.fill(path34, "evenodd");
 136 },
 137 () => {
 138 ctx.fillRect(1, 2, 3, 4);
 139 },
 140 () => {
 141 ctx.fillStyle;
 142 ctx.fillStyle = "test";
 143 ctx.fillStyle = linearGradient;
 144 ctx.fillStyle = radialGradient;
 145 ctx.fillStyle = pattern;
 146 },
 147 () => {
 148 ctx.fillText("testA", 1, 2);
 149 ctx.fillText("testB", 3, 4, 5);
 150 },
 151 () => {
 152 ctx.font;
 153 ctx.font = "test";
 154 },
 155 () => {
 156 e(() => ctx.getImageData(1, 2, 3, 4));
 157 },
 158 () => {
 159 ctx.getLineDash();
 160 },
 161 () => {
 162 ctx.getTransform();
 163 },
 164 () => {
 165 ctx.globalAlpha;
 166 ctx.globalAlpha = 0;
 167 },
 168 () => {
 169 ctx.globalCompositeOperation;
 170 ctx.globalCompositeOperation = "test";
 171 },
 172 () => {
 173 ctx.imageSmoothingEnabled;
 174 ctx.imageSmoothingEnabled = true;
 175 },
 176 () => {
 177 ctx.imageSmoothingQuality;
 178 ctx.imageSmoothingQuality = "low";
 179 },
 180 () => {
 181 ctx.isPointInPath(path12, 5, 6);
 182 ctx.isPointInPath(path34, 7, 8, "evenodd");
 183 ctx.isPointInPath(9, 10);
 184 ctx.isPointInPath(11, 12, "evenodd");
 185 },
 186 () => {
 187 ctx.isPointInStroke(path12, 3, 4);
 188 ctx.isPointInStroke(5, 6);
 189 },
 190 () => {
 191 ctx.lineCap;
 192 ctx.lineCap = "test";
 193 },
 194 () => {
 195 ctx.lineDashOffset;
 196 ctx.lineDashOffset = 1;
 197 },
 198 () => {
 199 ctx.lineJoin;
 200 ctx.lineJoin = "test";
 201 },
 202 () => {
 203 ctx.lineTo(1, 2);
 204 },
 205 () => {
 206 ctx.lineWidth;
 207 ctx.lineWidth = 1;
 208 },
 209 () => {
 210 ctx.measureText("test");
 211 },
 212 () => {
 213 ctx.miterLimit;
 214 ctx.miterLimit = 1;
 215 },
 216 () => {
 217 ctx.moveTo(1, 2);
 218 },
 219 () => {
 220 ctx.putImageData(imageData14, 5, 6);
 221 ctx.putImageData(imageData23, 7, 8, 9, 10, 11, 12);
 222 },
 223 () => {
 224 ctx.quadraticCurveTo(1, 2, 3, 4);
 225 },
 226 () => {
 227 ctx.rect(1, 2, 3, 4);
 228 },
 229 () => {
 230 ctx.resetTransform();
 231 },
 232 () => {
 233 ctx.restore();
 234 },
 235 () => {
 236 ctx.rotate(1);
 237 },
 238 () => {
 239 ctx.save();
 240 },
 241 () => {
 242 ctx.scale(1, 2);
 243 },
 244 () => {
 245 ctx.setAlpha();
 246 ctx.setAlpha(1);
 247 },
 248 () => {
 249 ctx.setCompositeOperation();
 250 ctx.setCompositeOperation("test");
 251 },
 252 () => {
 253 ctx.setFillColor("testA");
 254 ctx.setFillColor("testB", 1);
 255 ctx.setFillColor(2);
 256 ctx.setFillColor(3, 4);
 257 ctx.setFillColor(5, 6, 7, 8);
 258 ctx.setFillColor(9, 10, 11, 12, 13);
 259 },
 260 () => {
 261 ctx.setLineCap();
 262 ctx.setLineCap("test");
 263 },
 264 () => {
 265 ctx.setLineDash([1, 2]);
 266 },
 267 () => {
 268 ctx.setLineJoin();
 269 ctx.setLineJoin("test");
 270 },
 271 () => {
 272 ctx.setLineWidth();
 273 ctx.setLineWidth(1);
 274 },
 275 () => {
 276 ctx.setMiterLimit();
 277 ctx.setMiterLimit(1);
 278 },
 279 () => {
 280 ctx.setShadow(1, 2, 3);
 281 ctx.setShadow(4, 5, 6, "test", 7);
 282 ctx.setShadow(8, 9, 10, 11);
 283 ctx.setShadow(12, 13, 14, 15, 16);
 284 ctx.setShadow(17, 18, 19, 20, 21, 22, 23);
 285 ctx.setShadow(24, 25, 26, 27, 28, 29, 30, 31);
 286 },
 287 () => {
 288 ctx.setStrokeColor("testA");
 289 ctx.setStrokeColor("testB", 1);
 290 ctx.setStrokeColor(2);
 291 ctx.setStrokeColor(3, 4);
 292 ctx.setStrokeColor(5, 6, 7, 8);
 293 ctx.setStrokeColor(9, 10, 11, 12, 13);
 294 },
 295 () => {
 296 ctx.setTransform(1, 2, 3, 4, 5, 6);
 297 e(() => ctx.setTransform());
 298 e(() => ctx.setTransform(new DOMMatrix([7, 8, 9, 10, 11, 12])));
 299 },
 300 () => {
 301 ctx.shadowBlur;
 302 ctx.shadowBlur = 1;
 303 },
 304 () => {
 305 ctx.shadowColor;
 306 ctx.shadowColor = "test";
 307 },
 308 () => {
 309 ctx.shadowOffsetX;
 310 ctx.shadowOffsetX = 1;
 311 },
 312 () => {
 313 ctx.shadowOffsetY;
 314 ctx.shadowOffsetY = 1;
 315 },
 316 () => {
 317 ctx.stroke();
 318 ctx.stroke(path12);
 319 },
 320 () => {
 321 ctx.strokeRect(1, 2, 3, 4);
 322 },
 323 () => {
 324 ctx.strokeStyle;
 325 ctx.strokeStyle = "test";
 326 ctx.strokeStyle = linearGradient;
 327 ctx.strokeStyle = radialGradient;
 328 ctx.strokeStyle = pattern;
 329 },
 330 () => {
 331 ctx.strokeText("testA", 1, 2);
 332 ctx.strokeText("testB", 3, 4, 5);
 333 },
 334 () => {
 335 ctx.textAlign;
 336 ctx.textAlign = "test";
 337 },
 338 () => {
 339 ctx.textBaseline;
 340 ctx.textBaseline = "test";
 341 },
 342 () => {
 343 ctx.transform(1, 2, 3, 4, 5, 6);
 344 },
 345 () => {
 346 ctx.translate(1, 2);
 347 },
 348 () => {
 349 ctx.webkitBackingStorePixelRatio;
 350 },
 351 () => {
 352 e(() => ctx.webkitGetImageDataHD(1, 2, 3, 4));
 353 },
 354 () => {
 355 ctx.webkitImageSmoothingEnabled;
 356 ctx.webkitImageSmoothingEnabled = true;
 357 },
 358 () => {
 359 ctx.webkitLineDash;
 360 ctx.webkitLineDash = [1, 2];
 361 },
 362 () => {
 363 ctx.webkitLineDashOffset;
 364 ctx.webkitLineDashOffset = 1;
 365 },
 366 () => {
 367 ctx.webkitPutImageDataHD(imageData14, 5, 6);
 368 ctx.webkitPutImageDataHD(imageData23, 7, 8, 9, 10, 11, 12);
 369 },
 370 () => {
 371 TestPage.dispatchEventToFrontend("LastFrame");
 372 },
 373 ];
 374 let index = 0;
 375 function f() {
 376 frames[index++]();
 377 if (index < frames.length)
 378 animationFrameID = window.requestAnimationFrame(f);
 379 };
 380 f();
 381}
 382
 383function test() {
 384 let suite = InspectorTest.createAsyncSuite("Canvas.recording2D");
 385
 386 function requestRecording(parameters, resolve, reject) {
 387 let canvases = WebInspector.canvasManager.canvases.filter((canvas) => canvas.contextType === WebInspector.Canvas.ContextType.Canvas2D);
 388 if (!canvases.length) {
 389 reject("Missing 2D canvas.");
 390 return;
 391 }
 392
 393 InspectorTest.assert(canvases.length === 1, "There should only be one canvas-2d.");
 394
 395 WebInspector.canvasManager.awaitEvent(WebInspector.CanvasManager.Event.RecordingFinished)
 396 .then((event) => {
 397 InspectorTest.evaluateInPage(`cancelActions()`);
 398
 399 let json = event.data.recording.toJSON();
 400 json.data = json.data.map((item) => {
 401 if (typeof item !== "string" || !item.includes("/LayoutTests/inspector/canvas/recording-2d.html"))
 402 return item;
 403
 404 // The bots will fail since the path locally is most likely different than the bots.
 405 return "/LayoutTests/inspector/canvas/recording-2d.html";
 406 })
 407 InspectorTest.log(JSON.stringify(json, null, 2));
 408 })
 409 .then(resolve, reject);
 410
 411 CanvasAgent.requestRecording(canvases[0].identifier, ...parameters, (error) => {
 412 if (error) {
 413 reject(error);
 414 return;
 415 }
 416
 417 InspectorTest.evaluateInPage(`performActions()`, (error) => {
 418 if (error)
 419 reject(error);
 420 });
 421 });
 422
 423 return canvases[0];
 424 }
 425
 426 suite.addTestCase({
 427 name: "Canvas.recording2D.singleFrame",
 428 description: "Check that the recording is stopped after a single frame.",
 429 test(resolve, reject) {
 430 const singleFrame = true;
 431 let canvas = requestRecording([singleFrame], resolve, reject);
 432 }
 433 });
 434
 435 suite.addTestCase({
 436 name: "Canvas.recording2D.multipleFrames",
 437 description: "Check that recording data is serialized correctly for multiple frames.",
 438 test(resolve, reject) {
 439 const singleFrame = false;
 440 let canvas = requestRecording([singleFrame], resolve, reject);
 441
 442 InspectorTest.singleFireEventListener("LastFrame", () => {
 443 CanvasAgent.cancelRecording(canvas.identifier, (error) => {
 444 if (error) {
 445 reject(error);
 446 return;
 447 }
 448 });
 449 });
 450 }
 451 });
 452
 453 suite.addTestCase({
 454 name: "Canvas.recording2D.memoryLimit",
 455 description: "Check that the recording is stopped when it reaches the memory limit.",
 456 test(resolve, reject) {
 457 const singleFrame = false;
 458 const memoryLimit = 10;
 459 let canvas = requestRecording([singleFrame, memoryLimit], resolve, reject);
 460 }
 461 });
 462
 463 // ------
 464
 465 suite.addTestCase({
 466 name: "Canvas.requestRecording.InvalidCanvasId",
 467 description: "Invalid canvas identifiers should cause an error.",
 468 test(resolve, reject) {
 469 const canvasId = "DOES_NOT_EXIST";
 470 CanvasAgent.requestRecording(canvasId, (error) => {
 471 InspectorTest.expectThat(error, "Should produce an error.");
 472 InspectorTest.log("Error: " + error);
 473 resolve();
 474 });
 475 }
 476 });
 477
 478 suite.addTestCase({
 479 name: "Canvas.cancelRecording.InvalidCanvasId",
 480 description: "Invalid canvas identifiers should cause an error.",
 481 test(resolve, reject) {
 482 const canvasId = "DOES_NOT_EXIST";
 483 CanvasAgent.cancelRecording(canvasId, (error) => {
 484 InspectorTest.expectThat(error, "Should produce an error.");
 485 InspectorTest.log("Error: " + error);
 486 resolve();
 487 });
 488 }
 489 });
 490
 491 suite.runTestCasesAndFinish();
 492}
 493</script>
 494</head>
 495<body onload="load()">
 496 <p>Test that CanvasManager is able to record actions made to 2D canvas contexts.</p>
 497</body>
 498</html>

LayoutTests/inspector/model/recording-expected.txt

 1Testing the fault-tolerance of WebInspector.Recording.
 2
 3
 4== Running test suite: Recording
 5-- Running test case: Recording.fromPayload.nullObject
 6null
 7
 8-- Running test case: Recording.fromPayload.nonObject
 9null
 10
 11-- Running test case: Recording.fromPayload.emptyObject
 12null
 13
 14-- Running test case: Recording.fromPayload.invalidTopLevelMembers
 15null
 16
 17-- Running test case: Recording.fromPayload.invalidSubMembers
 18{
 19 "version": 1,
 20 "type": "test",
 21 "initialState": {},
 22 "frames": [
 23 {
 24 "actions": []
 25 }
 26 ],
 27 "data": [
 28 "test"
 29 ]
 30}
 31
 32-- Running test case: Recording.fromPayload.invalidFrame
 33{
 34 "version": 1,
 35 "type": "test",
 36 "initialState": {
 37 "attributes": {
 38 "test": "test"
 39 },
 40 "parameters": [
 41 "test"
 42 ],
 43 "content": "test"
 44 },
 45 "frames": [
 46 {
 47 "actions": []
 48 }
 49 ],
 50 "data": [
 51 "test"
 52 ]
 53}
 54
 55-- Running test case: Recording.fromPayload.invalidAction
 56{
 57 "version": 1,
 58 "type": "test",
 59 "initialState": {
 60 "attributes": {
 61 "test": "test"
 62 },
 63 "parameters": [
 64 "test"
 65 ],
 66 "content": "test"
 67 },
 68 "frames": [
 69 {
 70 "actions": [
 71 [
 72 null,
 73 [],
 74 []
 75 ]
 76 ],
 77 "incomplete": true
 78 }
 79 ],
 80 "data": [
 81 "test"
 82 ]
 83}
 84
 85-- Running test case: Recording.fromPayload.invalidActionMembers
 86{
 87 "version": 1,
 88 "type": "test",
 89 "initialState": {
 90 "attributes": {
 91 "test": "test"
 92 },
 93 "parameters": [
 94 "test"
 95 ],
 96 "content": "test"
 97 },
 98 "frames": [
 99 {
 100 "actions": [
 101 [
 102 null,
 103 [],
 104 []
 105 ]
 106 ],
 107 "incomplete": true
 108 }
 109 ],
 110 "data": [
 111 "test"
 112 ]
 113}
 114
 115-- Running test case: Recording.fromPayload.valid
 116{
 117 "version": 1,
 118 "type": "test",
 119 "initialState": {
 120 "attributes": {
 121 "test": "test"
 122 },
 123 "parameters": [
 124 "test"
 125 ],
 126 "content": "test"
 127 },
 128 "frames": [
 129 {
 130 "actions": [
 131 [
 132 0,
 133 [
 134 0
 135 ],
 136 [
 137 0
 138 ]
 139 ]
 140 ],
 141 "incomplete": true
 142 }
 143 ],
 144 "data": [
 145 "test"
 146 ]
 147}
 148

LayoutTests/inspector/model/recording.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
 5<script>
 6function test()
 7{
 8 let suite = InspectorTest.createSyncSuite("Recording");
 9
 10 function addTest({name, payload}) {
 11 suite.addTestCase({
 12 name,
 13 test() {
 14 let recording = WebInspector.Recording.fromPayload(payload);
 15 InspectorTest.log(recording ? JSON.stringify(recording.toJSON(), null, 2) : recording);
 16 return true;
 17 }
 18 });
 19 }
 20
 21 let tests = [
 22 {
 23 name: "Recording.fromPayload.nullObject",
 24 payload: null,
 25 },
 26 {
 27 name: "Recording.fromPayload.nonObject",
 28 payload: "INVALID",
 29 },
 30 {
 31 name: "Recording.fromPayload.emptyObject",
 32 payload: {},
 33 },
 34 {
 35 name: "Recording.fromPayload.invalidTopLevelMembers",
 36 payload: {
 37 version: null,
 38 type: null,
 39 initialState: null,
 40 frames: null,
 41 data: null,
 42 },
 43 },
 44 {
 45 name: "Recording.fromPayload.invalidSubMembers",
 46 payload: {
 47 version: 1,
 48 type: "test",
 49 initialState: {
 50 attributes: null,
 51 parameters: null,
 52 content: null,
 53 },
 54 frames: [null],
 55 data: ["test"],
 56 },
 57 },
 58 {
 59 name: "Recording.fromPayload.invalidFrame",
 60 payload: {
 61 version: 1,
 62 type: "test",
 63 initialState: {
 64 attributes: {
 65 test: "test",
 66 },
 67 parameters: ["test"],
 68 content: "test",
 69 },
 70 frames: [
 71 {
 72 actions: null,
 73 incomplete: null,
 74 },
 75 ],
 76 data: ["test"],
 77 },
 78 },
 79 {
 80 name: "Recording.fromPayload.invalidAction",
 81 payload: {
 82 version: 1,
 83 type: "test",
 84 initialState: {
 85 attributes: {
 86 test: "test",
 87 },
 88 parameters: ["test"],
 89 content: "test",
 90 },
 91 frames: [
 92 {
 93 actions: [null],
 94 incomplete: true,
 95 },
 96 ],
 97 data: ["test"],
 98 },
 99 },
 100 {
 101 name: "Recording.fromPayload.invalidActionMembers",
 102 payload: {
 103 version: 1,
 104 type: "test",
 105 initialState: {
 106 attributes: {
 107 test: "test",
 108 },
 109 parameters: ["test"],
 110 content: "test",
 111 },
 112 frames: [
 113 {
 114 actions: [
 115 [
 116 null,
 117 null,
 118 null,
 119 ],
 120 ],
 121 incomplete: true,
 122 },
 123 ],
 124 data: ["test"],
 125 },
 126 },
 127 {
 128 name: "Recording.fromPayload.valid",
 129 payload: {
 130 version: 1,
 131 type: "test",
 132 initialState: {
 133 attributes: {
 134 test: "test",
 135 },
 136 parameters: ["test"],
 137 content: "test",
 138 },
 139 frames: [
 140 {
 141 actions: [
 142 [
 143 0,
 144 [0],
 145 [0],
 146 ],
 147 ],
 148 incomplete: true,
 149 },
 150 ],
 151 data: ["test"],
 152 },
 153 },
 154 ];
 155 tests.forEach(addTest);
 156
 157 suite.runTestCasesAndFinish();
 158}
 159</script>
 160</head>
 161<body onload="runTest()">
 162 <p>Testing the fault-tolerance of WebInspector.Recording.</p>
 163</body>
 164</html>