Commit 6f33f277 authored by Belén Albeza's avatar Belén Albeza
Browse files

Bug 1715579 - [devtools] Add architecture documentation for the storage panel r=jdescottes

This documents the new architecture of the storage panel.

Differential Revision: https://phabricator.services.mozilla.com/D117334
parent b946d3b8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
  * [Debugger](tools/debugger-panel.md)
  * [Responsive Design Mode](tools/responsive-design-mode.md)
  * [Console](tools/console-panel.md)
  * [Storage](tools/storage.md)
* [Frontend](frontend/frontend.md)
  * [Panel SVGs](frontend/svgs.md)
  * [React](frontend/react.md)
+1 −0
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ Tool Architectures
   Responsive Design Mode <tools/responsive-design-mode.md>
   Console <tools/console-panel.md>
   Network </devtools/netmonitor/architecture.md>
   Storage <tools/storage.md>


Frontend
+90 −0
Original line number Diff line number Diff line
# Storage Panel Architecture

## Actor structure

### Legacy

This is currently only used by the browser toolbox and when inspecting Web Extensions.

![Class structure architecture (legacy)](storage/legacy.svg)

- We have an actor per storage type.
- These actors are contained in a pool managed by a global `Storage` actor. See [source code of the actor](https://searchfox.org/mozilla-central/rev/2c991232499e826e46f9d976eb653817340ba389/devtools/server/actors/storage.js#3435) and [source code of the pool](https://searchfox.org/mozilla-central/rev/2c991232499e826e46f9d976eb653817340ba389/devtools/server/actors/storage.js#3477-3489)
- Each specific storage type actor has a reference back to this global `Storage` actor.

### Resource-based

This is the new architecture that is being implemented to support Fission. It's currently used when inspecting tabs.

![Class structure architecture (resource-based)](storage/resources.svg)

- We no longer have a global `Storage` actor.
- The specific actors for each storage type are spawned by watchers instead.
- The reference to a global `Storage` actor that each actor has now points to a mock instead.
- Some watchers require to be run in the parent process, while others can be run in the content process.
  - Parent process: Cookies, IndexedDB, Web Extension[^web-extension-not-implemented].
  - Content process: LocalStorage, SessionStorage, Cache.

[^web-extension-not-implemented]: Web Extension has not yet been implemented in this new architecture.

## Flow

Some considerations to keep in mind:

- In the Storage Panel, **resources are fronts**.
- These fronts contain a `hosts` object, which is populated with the host name, and the actual storage data it contains.
- In the client, we get as part of the `onAvailable` callback of `ResourceCommand.watchResources`:
  - Content process storage types: multiple resources, one per target
  - Parent process storage types: a single resource

### Initial load

Web page loaded, open toolbox. Later on, we see what happens if a new remote target is added (for instance, an iframe is created that points to a different host).

#### Fission OFF

![Initial load diagram, fission off](storage/flow-fission-off.svg)

- We get all the storage fronts as new resources sent in the `onAvailable` callback for `watchResources`.
- After a remote target has been added, we get new additions as `"single-store-update"` events.

#### Fission ON

![Initial load diagram, fission on](storage/flow-fission-on.svg)

Similar to the previous scenario (fission off), but now when a new remote target is added:

- We get content process storage resources in a new `onAvailable` callback, instead of `"single-store-update"`.
- Parent process storage resources keep using the `"single-store-update"` method. This is possible due to their `StorageMock` actors emitting a fake `"window-ready"` event after a `"window-global-created"`.

### Navigation

#### Fission ON, target switching OFF

![Navigation diagram, fission on, target switching off](storage/navigation-fission-on-target-switching-off.svg)

- Deletion of content process storage hosts is handled within the `onTargetDestroyed` callback.
- Deletion of parent process storage hosts is handled with `"single-store-update"` events, fired when the `StorageMock` detects a `"window-global-destroyed"` event.
- When the new target is available, new storage actors are spawned from their watchers' `watch` method and are sent as resources in the `onAvailable` callback.

#### Fission ON, target switching ON

![Navigation diagram, fission on, target switching off](storage/navigation-fission-on-target-switching-on.svg)

Similar to the previous scenario (fission on, target switching off), but parent process storage resources are handled differently, since their watchers remain instantiated.

- New actors for parent process resources are not spawned by their watchers `watch`, but as a callback of `"window-global-created"`.
- Some times there's a race condition between a target being available and firing `"window-global-created"`. There is a delay to send the resource to the client, to ensure that any `onTargetAvailable` callback is processed first.
  - The new actor/resource is sent after a `"target-available-form"` event.

### CRUD operations

#### Add a new cookie

Other CRUD operations work very similar to this one.

![CRUD operation diagram, add a new cookie](storage/crud-cookie.svg)

- We call `StorageMock.getWindowFromHost` so we can get the storage principal. Since this is a parent process resource, it doesn't have access to an actual window, so it returns a mock instead (but with a real principal).
- To detect changes in storage, we subscribe to different events that platform provides via `Services.obs.addObserver`.
- To manipulate storage data, we use different methods depending on the storage type. For cookies, we use the API provided by `Services.cookies`.
 No newline at end of file
+6 −0
Original line number Diff line number Diff line
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1052px" height="527px" viewBox="-0.5 -0.5 1052 527" content="&lt;mxfile host=&quot;Electron&quot; modified=&quot;2021-06-09T15:53:37.452Z&quot; agent=&quot;5.0 (Macintosh; Intel Mac OS X 11_4_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36&quot; version=&quot;14.6.13&quot; etag=&quot;EwVAYTYE5uQF3ssIPhho&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;Dm-Yc-wTLX_n6glO8A4T&quot;&gt;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&lt;/diagram&gt;&lt;/mxfile&gt;" style="background-color: rgb(255, 255, 255);"><defs/><g><rect x="555" y="0" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 630 60 L 630 525" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="629.5" y="26.5">StorageActorMock</text><text x="629.5" y="47.5">(parent p.)</text></g><rect x="622.5" y="330" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="135" y="0" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 210 60 L 210 525" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="209.5" y="37">StorageUI</text></g><rect x="202.5" y="116.25" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 21 116.25 L 190.32 116.25" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="15" cy="116.25" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 200.82 116.25 L 190.32 121.5 L 190.32 111 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="43" y="74" width="145" height="41" stroke-width="0"/><text x="113.5" y="88.75">user clicks the</text><text x="113.5" y="108.25">"add cookie" button</text></g><rect x="720" y="0" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 795 60 L 795 525" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="794.5" y="26.5">Cookies</text><text x="794.5" y="47.5">(actor)</text></g><rect x="787.5" y="135" width="15" height="90" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="787.5" y="270" width="15" height="90" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="390" y="0" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 465 60 L 465 495" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="464.5" y="37">CookieWatcher</text></g><rect x="457.5" y="375" width="15" height="106.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="622.5" y="375" width="15" height="46.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 621.75 375 L 483.93 375" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 473.43 375 L 483.93 369.75 L 483.93 380.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 247px; margin-left: 365px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoresUpdate</div></div></div></foreignObject><text x="365" y="247" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoresUpdate</text></switch></g><rect x="510" y="375" width="75" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 260px; margin-left: 341px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">throttled</div></div></div></foreignObject><text x="365" y="262" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">throttled</text></switch></g><rect x="787.5" y="447.75" width="15" height="63.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 471.75 449.23 L 773.82 450.69" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 784.32 450.74 L 773.8 455.94 L 773.85 445.44 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 297px; margin-left: 419px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">emit("single-store-update")</div></div></div></foreignObject><text x="419" y="297" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">emit("single-sto...</text></switch></g><rect x="202.5" y="462.75" width="15" height="48.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 785.25 495.75 L 228.18 495.52" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 217.68 495.51 L 228.18 490.27 L 228.17 500.77 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 327px; margin-left: 250px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoreUpdate</div></div></div></foreignObject><text x="250" y="327" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoreUpdate</text></switch></g><rect x="570" y="447.75" width="135" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 309px; margin-left: 381px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">one event per update</div></div></div></foreignObject><text x="425" y="311" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">one event per update</text></switch></g><path d="M 216.75 134.25 L 775.32 134.98" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 785.82 135 L 775.32 140.23 L 775.33 129.73 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 87px; margin-left: 334px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">addItem</div></div></div></foreignObject><text x="334" y="87" fill="#000000" font-family="Helvetica" font-size="9px" text-anchor="middle">addItem</text></switch></g><rect x="622.5" y="165" width="15" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 787.5 164.25 L 648.18 164.94" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 637.68 164.99 L 648.15 159.69 L 648.2 170.19 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 107px; margin-left: 474px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">getWindowFromHost</div></div></div></foreignObject><text x="474" y="107" fill="#000000" font-family="Helvetica" font-size="9px" text-anchor="middle">getWindowFromHost</text></switch></g><path d="M 638.25 194.25 L 784.15 194.25" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="stroke"/><path d="M 772.32 201 L 785.82 194.25 L 772.32 187.5" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 127px; margin-left: 476px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">window mock</div></div></div></foreignObject><text x="476" y="127" fill="#000000" font-family="Helvetica" font-size="9px" text-anchor="middle">window mock</text></switch></g><rect x="900" y="0" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 975 60 L 975 525" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="974.5" y="37">Services</text></g><rect x="967.5" y="210" width="15" height="15" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="967.5" y="255" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 801.75 208.53 L 953.07 209.89" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 963.57 209.98 L 953.03 215.14 L 953.12 204.64 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 137px; margin-left: 589px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">cookies.add</div></div></div></foreignObject><text x="589" y="137" fill="#000000" font-family="Helvetica" font-size="9px" text-anchor="middle">cookies.add</text></switch></g><path d="M 966.75 270 L 813.18 270" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 802.68 270 L 813.18 264.75 L 813.18 275.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 177px; margin-left: 590px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onCookieChanged</div></div></div></foreignObject><text x="590" y="177" fill="#000000" font-family="Helvetica" font-size="9px" text-anchor="middle">onCookieChanged</text></switch></g><path d="M 785.25 330.03 L 650.43 330" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 639.93 330 L 650.43 324.75 L 650.43 335.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 217px; margin-left: 475px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">update("added")</div></div></div></foreignObject><text x="475" y="217" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">update("added")</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
 No newline at end of file
+6 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading