Skip to content

Commit

Permalink
feat: mweb parser won't produce shadow root context with random id
Browse files Browse the repository at this point in the history
  • Loading branch information
alsakhaev committed Aug 1, 2024
1 parent e92487a commit 1f22623
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 18 deletions.
49 changes: 42 additions & 7 deletions libs/core/src/adapters/dynamic-html-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { IParser } from '../parsers/interface'
import { IContextNode, ITreeBuilder, InsertionPointWithElement } from '../tree/types'
import { IAdapter } from './interface'

const ShadowHostAttr = 'data-mweb-shadow-host'

export class DynamicHtmlAdapter implements IAdapter {
protected element: HTMLElement
protected treeBuilder: ITreeBuilder
Expand All @@ -22,7 +24,7 @@ export class DynamicHtmlAdapter implements IAdapter {
this.parser = parser

// Namespace is used as ID for the root context
this.context = this._tryCreateContextForElement(element, 'root', this.namespace)
this.context = this._tryCreateContextForElement(element, 'root', 'global')
}

start() {
Expand All @@ -35,7 +37,7 @@ export class DynamicHtmlAdapter implements IAdapter {
})

// initial parsing without waiting for mutations in the DOM
this._handleMutations(element, this.#contextByElement.get(element)!)
this._handleMutations([], observer, element, this.#contextByElement.get(element)!)
})
this.#isStarted = true
}
Expand Down Expand Up @@ -75,7 +77,9 @@ export class DynamicHtmlAdapter implements IAdapter {
element
)

const observer = new MutationObserver(() => this._handleMutations(element, context))
const observer = new MutationObserver((mutations, observer) =>
this._handleMutations(mutations, observer, element, context)
)

this.#observerByElement.set(element, observer)
this.#elementByContext.set(context, element)
Expand All @@ -94,20 +98,30 @@ export class DynamicHtmlAdapter implements IAdapter {
return context
}

private _handleMutations(element: HTMLElement, context: IContextNode) {
private _handleMutations(
mutations: MutationRecord[],
observer: MutationObserver,
element: HTMLElement,
context: IContextNode
) {
const parsedContext = this.parser.parseContext(element, context.contextType)
const pairs = this.parser.findChildElements(element, context.contextType)
const insPoints = this._findAvailableInsPoints(element, context.contextType)

this.treeBuilder.updateParsedContext(context, parsedContext)
this.treeBuilder.updateInsertionPoints(context, insPoints)
this._removeOldChildContexts(pairs, context)
this._appendNewChildContexts(pairs, context)
this._appendNewChildContexts(pairs, context, observer)

if (this.parser.shouldParseShadowDom) {
this._observeShadowRoots(mutations, observer)
}
}

private _appendNewChildContexts(
childPairs: { element: HTMLElement; contextName: string }[],
parentContext: IContextNode
parentContext: IContextNode,
observer: MutationObserver
) {
for (const { element, contextName } of childPairs) {
if (!this.#contextByElement.has(element)) {
Expand All @@ -121,7 +135,7 @@ export class DynamicHtmlAdapter implements IAdapter {
this.treeBuilder.appendChild(parentContext, childContext)

// initial parsing
this._handleMutations(element, childContext)
this._handleMutations([], observer, element, childContext)
}
}
}
Expand All @@ -141,6 +155,27 @@ export class DynamicHtmlAdapter implements IAdapter {
}
}

private _observeShadowRoots(mutations: MutationRecord[], observer: MutationObserver) {
mutations.forEach((mutation) => {
if (mutation.type !== 'childList') return

mutation.addedNodes.forEach((node) => {
if (node.nodeType !== Node.ELEMENT_NODE) return
if (!(node instanceof Element)) return
if (!node.shadowRoot) return
if (!node.hasAttribute(ShadowHostAttr)) return // ToDo: it's mweb-parser specific logic

// ToDo: the similar logic as in _tryCreateContextForElement
observer.observe(node.shadowRoot, {
attributes: true,
childList: true,
subtree: true,
characterData: true,
})
})
})
}

// ToDo: move to parser?
private _findAvailableInsPoints(
element: HTMLElement,
Expand Down
3 changes: 3 additions & 0 deletions libs/core/src/parsers/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export type InsertionPoint = {
}

export interface IParser {

shouldParseShadowDom?: boolean

parseContext(element: HTMLElement, contextName: string): any

findChildElements(
Expand Down
3 changes: 3 additions & 0 deletions libs/core/src/parsers/mweb-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ const ShadowHostAttr = 'data-mweb-shadow-host'
const LayoutManagerAttr = 'data-mweb-layout-manager'

export class MutableWebParser implements IParser {
shouldParseShadowDom = true

parseContext(element: HTMLElement, contextName: string) {
if (contextName === 'root') return { id: 'global' }
const json = element.getAttribute(ParsedContextAttr)
if (!json) return {}
return JSON.parse(json)
Expand Down
11 changes: 0 additions & 11 deletions libs/engine/src/app/components/shadow-dom-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ import { createPortal } from 'react-dom'
import { StyleSheetManager } from 'styled-components'
import { StyleProvider } from '@ant-design/cssinjs'

const generateGuid = () => {
return Array.from(crypto.getRandomValues(new Uint8Array(16)))
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
}

export interface ShadowDomWrapperProps {
children?: React.ReactNode
stylesheetSrc?: string
Expand Down Expand Up @@ -68,11 +62,6 @@ export const ShadowDomWrapper = React.forwardRef<HTMLDivElement, ShadowDomWrappe
// For mweb parser that looks for contexts in shadow dom
myRef.current.setAttribute('data-mweb-shadow-host', '')

// Context cannot be a shadow root node because mutation observer doesn't work there
// So we need to select a child node for context
container.setAttribute('data-mweb-context-type', 'shadow-dom')
container.setAttribute('data-mweb-context-parsed', `{"id":"${generateGuid()}"}`)

shadowRoot.appendChild(container)

// Prevent event propagation from BOS-component to parent
Expand Down

0 comments on commit 1f22623

Please sign in to comment.