<commit_before>/// <reference path='./index.d.ts'/>

import * as StackTrace from 'stacktrace-js'
import ActionMap from './private/ActionMap'
import ActionTagMap from './private/ActionTagMap'
import Anomalies from './private/Anomalies'
import OwnerManager from './private/OwnerManager'
import ShadowElement from './private/ShadowElement'
import {
  attachAttr,
  sendActionInfoToContentscript,
  setAttrValue
} from './private/NativeUtils'

setupShadowElement()
setupWindow()
setupDocument()
trackGeneralCases()
trackHTMLElementAnomalies()
trackElementAnomalies()
trackAttrAnomalies()
trackNamedNodeMapAnomalies()

/**
 * register custom elements
 */

function setupShadowElement() {
  customElements.define(
    ShadowElement.TagName,
    ShadowElement
  )
}

function setupWindow(): void {
  setupNonElementTarget(window, 'window')
}

function setupDocument(): void {
  setupNonElementTarget(document, 'document')
}

function setupNonElementTarget(target: ActionTarget, name: string) {
  const infoElementName = `${name}-info`

  customElements.define(
    infoElementName,
    class extends HTMLElement { }
  )
  const infoElement =
    document.createElement(infoElementName)

  OwnerManager.setOwner(target, infoElement)
  document.documentElement.appendChild(infoElement)
}

/**
 * tracker utils
 */

function trackTemplate(
  template: {
    target: Target,
    action: Action,
    decorator: (
      target: Target,
      action: Action,
      actionFunc: (this: ActionTarget, ...args: any[]) => any
    ) => (this: ActionTarget, ...args: any[]) => any,
    getter?: boolean
  }
) {
  const { target, action, decorator } = template
  const shouldTrackGetter = template.getter
  const descriptor =
    Reflect.getOwnPropertyDescriptor(window[target].prototype, action)
  // @NOTE: getter, setter, method are mutual exclusive
  if (shouldTrackGetter && hasGetter(descriptor)) {
    descriptor.get =
      decorator(target, action, descriptor.get)
  } else if (hasSetter(descriptor)) {
    descriptor.set =
      decorator(target, action, descriptor.set)
  } else if (hasMethod(descriptor)) {
    descriptor.value =
      decorator(target, action, descriptor.value)
  }
  Reflect.defineProperty(window[target].prototype, action, descriptor)
}

function hasGetter(descriptor: PropertyDescriptor): boolean {
  return !!descriptor.get
}

function hasSetter(descriptor: PropertyDescriptor): boolean {
  return !!descriptor.set
}

function hasMethod(descriptor: PropertyDescriptor): boolean {
  return !!descriptor.value && (typeof descriptor.value === 'function')
}

function record(
  data: {
    caller: ActionTarget,
    target: Target,
    action: Action,
    args?: any[],
    merge?: string
  }
): void {
  // @NOTE: target should not be derived from the type of caller
  // e.g., {
  //  caller: HTMLDivElement, 
  //  target: Element, 
  //  action: id
  // }
  if (!OwnerManager.hasOwner(data.caller)) {
    // @NOTE: although typescript predefine that caller should be ActionTarget,
    // caller is actually determined in runtime, and it's possible to get invalid 
    // callers, e.g., DocumentFragment, XHRHttpRequst
    return
  }
  const owner = OwnerManager.getOwner(data.caller)

  if (!owner.hasTrackID()) {
    owner.setTrackID()
  }
  const stackframe = filterStackTrace(StackTrace.getSync())

  sendActionInfoToContentscript(
    <ActionInfo>{
      trackid: owner.getTrackID(),
      target: data.target,
      action: data.action,
      actionTag: ActionTagMap.fetchActionTag(data),
      merge: data.merge,
      loc: {
        scriptUrl: stackframe.fileName,
        lineNumber: stackframe.lineNumber,
        columnNumber: stackframe.columnNumber
      },
    }
  )
}

const HTML_DOM_API_FRAME_INDEX = 2

function filterStackTrace(stacktrace: StackTrace.StackFrame[]): StackTrace.StackFrame {
  return stacktrace[HTML_DOM_API_FRAME_INDEX]
}

/**
 * trackGeneralCases
 */

function trackGeneralCases(): void {
  ActionMap.visit(function (target) {
    const proto = window[target].prototype

    Object.getOwnPropertyNames(proto).forEach((action) => {
      if (ActionMap.has(target, action) && !Anomalies.has(target, action)) {
        trackTemplate({ target, action, decorator })<commit_msg>refactor process of creating SourceLocation in record<commit_after>/// <reference path='./index.d.ts'/>

import * as StackTrace from 'stacktrace-js'
import ActionMap from './private/ActionMap'
import ActionTagMap from './private/ActionTagMap'
import Anomalies from './private/Anomalies'
import OwnerManager from './private/OwnerManager'
import ShadowElement from './private/ShadowElement'
import {
  attachAttr,
  sendActionInfoToContentscript,
  setAttrValue
} from './private/NativeUtils'

setupShadowElement()
setupWindow()
setupDocument()
trackGeneralCases()
trackHTMLElementAnomalies()
trackElementAnomalies()
trackAttrAnomalies()
trackNamedNodeMapAnomalies()

/**
 * register custom elements
 */

function setupShadowElement() {
  customElements.define(
    ShadowElement.TagName,
    ShadowElement
  )
}

function setupWindow(): void {
  setupNonElementTarget(window, 'window')
}

function setupDocument(): void {
  setupNonElementTarget(document, 'document')
}

function setupNonElementTarget(target: ActionTarget, name: string) {
  const infoElementName = `${name}-info`

  customElements.define(
    infoElementName,
    class extends HTMLElement { }
  )
  const infoElement =
    document.createElement(infoElementName)

  OwnerManager.setOwner(target, infoElement)
  document.documentElement.appendChild(infoElement)
}

/**
 * tracker utils
 */

function trackTemplate(
  template: {
    target: Target,
    action: Action,
    decorator: (
      target: Target,
      action: Action,
      actionFunc: (this: ActionTarget, ...args: any[]) => any
    ) => (this: ActionTarget, ...args: any[]) => any,
    getter?: boolean
  }
) {
  const { target, action, decorator } = template
  const shouldTrackGetter = template.getter
  const descriptor =
    Reflect.getOwnPropertyDescriptor(window[target].prototype, action)
  // @NOTE: getter, setter, method are mutual exclusive
  if (shouldTrackGetter && hasGetter(descriptor)) {
    descriptor.get =
      decorator(target, action, descriptor.get)
  } else if (hasSetter(descriptor)) {
    descriptor.set =
      decorator(target, action, descriptor.set)
  } else if (hasMethod(descriptor)) {
    descriptor.value =
      decorator(target, action, descriptor.value)
  }
  Reflect.defineProperty(window[target].prototype, action, descriptor)
}

function hasGetter(descriptor: PropertyDescriptor): boolean {
  return !!descriptor.get
}

function hasSetter(descriptor: PropertyDescriptor): boolean {
  return !!descriptor.set
}

function hasMethod(descriptor: PropertyDescriptor): boolean {
  return !!descriptor.value && (typeof descriptor.value === 'function')
}

function record(
  data: {
    caller: ActionTarget,
    target: Target,
    action: Action,
    args?: any[],
    merge?: string
  }
): void {
  // @NOTE: target should not be derived from the type of caller
  // e.g., {
  //  caller: HTMLDivElement, 
  //  target: Element, 
  //  action: id
  // }
  if (!OwnerManager.hasOwner(data.caller)) {
    // @NOTE: although typescript predefine that caller should be ActionTarget,
    // caller is actually determined in runtime, and it's possible to get invalid 
    // callers, e.g., DocumentFragment, XHRHttpRequst
    return
  }
  const owner = OwnerManager.getOwner(data.caller)

  if (!owner.hasTrackID()) {
    owner.setTrackID()
  }
  sendActionInfoToContentscript(
    <ActionInfo>{
      trackid: owner.getTrackID(),
      target: data.target,
      action: data.action,
      actionTag: ActionTagMap.fetchActionTag(data),
      merge: data.merge,
      loc: createSourceLocationFromStackFrame(
        filterStackTrace(StackTrace.getSync())
      ),
    }
  )
}

function createSourceLocationFromStackFrame(stackframe: StackTrace.StackFrame): SourceLocation {
  return {
    scriptUrl: stackframe.fileName,
    lineNumber: stackframe.lineNumber,
    columnNumber: stackframe.columnNumber
  }
}

const HTML_DOM_API_FRAME_INDEX = 2

function filterStackTrace(stacktrace: StackTrace.StackFrame[]): StackTrace.StackFrame {
  return stacktrace[HTML_DOM_API_FRAME_INDEX]
}

/**
 * trackGeneralCases
 */

function trackGeneralCases(): void {
  ActionMap.visit(function (target) {
    const proto = window[target].prototype

    Object.getOwnPropertyNames(proto).forEach((action) => {
      if (ActionMap.has(target, action) && !Anomalies.has(target, action)) {
        trackTemplate({ target, action, decorator })
{'fraction_non_alphanumeric': 0.11466449587161298, 'fraction_numerical': 0.00023258518432375856, 'mean_word_length': 4.064782096584216, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3584553', 'n_tokens_mistral': 2503, 'n_tokens_neox': 2483, 'n_words': 582}