added Linux arm64 SDK
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.AlignmentData = class AlignmentData
|
||||
{
|
||||
constructor(propertyName, text)
|
||||
{
|
||||
console.assert(Object.values(WI.AlignmentData.Type).includes(propertyName), propertyName);
|
||||
|
||||
this._type = WI.AlignmentData._propertyNameToType(propertyName);
|
||||
this._propertyName = propertyName;
|
||||
this._text = text;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static isAlignmentAwarePropertyName(propertyName)
|
||||
{
|
||||
return !!WI.AlignmentData._propertyNameToType(propertyName);
|
||||
}
|
||||
|
||||
static _propertyNameToType(propertyName)
|
||||
{
|
||||
switch (propertyName) {
|
||||
case "align-content":
|
||||
return WI.AlignmentData.Type.AlignContent;
|
||||
case "align-items":
|
||||
return WI.AlignmentData.Type.AlignItems;
|
||||
case "align-self":
|
||||
return WI.AlignmentData.Type.AlignSelf;
|
||||
case "justify-content":
|
||||
return WI.AlignmentData.Type.JustifyContent;
|
||||
case "justify-items":
|
||||
return WI.AlignmentData.Type.JustifyItems;
|
||||
case "justify-self":
|
||||
return WI.AlignmentData.Type.JustifySelf;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get type() { return this._type; }
|
||||
|
||||
get text() { return this._text; }
|
||||
set text(text) { this._text = text; }
|
||||
|
||||
toString()
|
||||
{
|
||||
return this._text;
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: <https://webkit.org/b/233055> Web Inspector: Add a swatch for justify-content, justify-items, and justify-self
|
||||
WI.AlignmentData.Type = {
|
||||
AlignContent: "align-content",
|
||||
AlignItems: "align-items",
|
||||
AlignSelf: "align-self",
|
||||
JustifyContent: "justify-content",
|
||||
JustifyItems: "justify-items",
|
||||
JustifySelf: "justify-self",
|
||||
};
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Animation = class Animation extends WI.Object
|
||||
{
|
||||
constructor(animationId, {name, cssAnimationName, cssTransitionProperty, effect, stackTrace} = {})
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(animationId);
|
||||
console.assert((!cssAnimationName && !cssTransitionProperty) || !!cssAnimationName !== !!cssTransitionProperty);
|
||||
console.assert(!stackTrace || stackTrace instanceof WI.StackTrace, stackTrace);
|
||||
|
||||
this._animationId = animationId;
|
||||
|
||||
this._name = name || null;
|
||||
this._cssAnimationName = cssAnimationName || null;
|
||||
this._cssTransitionProperty = cssTransitionProperty || null;
|
||||
this._updateEffect(effect);
|
||||
this._stackTrace = stackTrace || null;
|
||||
|
||||
this._effectTarget = undefined;
|
||||
this._requestEffectTargetCallbacks = null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromPayload(payload)
|
||||
{
|
||||
// COMPATIBILITY (macOS 13.0, iOS 16.0): `backtrace` was renamed to `stackTrace`.
|
||||
if (payload.backtrace)
|
||||
payload.stackTrace = {callFrames: payload.backtrace};
|
||||
|
||||
return new WI.Animation(payload.animationId, {
|
||||
name: payload.name,
|
||||
cssAnimationName: payload.cssAnimationName,
|
||||
cssTransitionProperty: payload.cssTransitionProperty,
|
||||
effect: payload.effect,
|
||||
stackTrace: WI.StackTrace.fromPayload(WI.assumingMainTarget(), payload.stackTrace),
|
||||
});
|
||||
}
|
||||
|
||||
static displayNameForAnimationType(animationType, plural)
|
||||
{
|
||||
switch (animationType) {
|
||||
case WI.Animation.Type.WebAnimation:
|
||||
return plural ? WI.UIString("Web Animations") : WI.UIString("Web Animation");
|
||||
case WI.Animation.Type.CSSAnimation:
|
||||
return plural ? WI.UIString("CSS Animations") : WI.UIString("CSS Animation");
|
||||
case WI.Animation.Type.CSSTransition:
|
||||
return plural ? WI.UIString("CSS Transitions") : WI.UIString("CSS Transition");
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown animation type", animationType);
|
||||
return null;
|
||||
}
|
||||
|
||||
static displayNameForPlaybackDirection(playbackDirection)
|
||||
{
|
||||
switch (playbackDirection) {
|
||||
case WI.Animation.PlaybackDirection.Normal:
|
||||
return WI.UIString("Normal", "Web Animation Playback Direction Normal", "Indicates that the playback direction of this web animation is normal (e.g. forwards)");
|
||||
case WI.Animation.PlaybackDirection.Reverse:
|
||||
return WI.UIString("Reverse", "Web Animation Playback Direction Reverse", "Indicates that the playback direction of this web animation is reversed (e.g. backwards)");
|
||||
case WI.Animation.PlaybackDirection.Alternate:
|
||||
return WI.UIString("Alternate", "Web Animation Playback Direction Alternate", "Indicates that the playback direction of this web animation alternates between normal and reversed on each iteration");
|
||||
case WI.Animation.PlaybackDirection.AlternateReverse:
|
||||
return WI.UIString("Alternate Reverse", "Web Animation Playback Direction Alternate Reverse", "Indicates that the playback direction of this web animation alternates between reversed and normal on each iteration");
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown playback direction", playbackDirection);
|
||||
return null;
|
||||
}
|
||||
|
||||
static displayNameForFillMode(fillMode)
|
||||
{
|
||||
switch (fillMode) {
|
||||
case WI.Animation.FillMode.None:
|
||||
return WI.UIString("None", "Web Animation Fill Mode None", "Indicates that this web animation does not apply any styles before it begins and after it ends");
|
||||
case WI.Animation.FillMode.Forwards:
|
||||
return WI.UIString("Forwards", "Web Animation Fill Mode Forwards", "Indicates that this web animation also applies styles after it ends");
|
||||
case WI.Animation.FillMode.Backwards:
|
||||
return WI.UIString("Backwards", "Web Animation Fill Mode Backwards", "Indicates that this web animation also applies styles before it begins");
|
||||
case WI.Animation.FillMode.Both:
|
||||
return WI.UIString("Both", "Web Animation Fill Mode Both", "Indicates that this web animation also applies styles before it begins and after it ends");
|
||||
case WI.Animation.FillMode.Auto:
|
||||
return WI.UIString("Auto", "Web Animation Fill Mode Auto", "Indicates that this web animation either does not apply any styles before it begins and after it ends or that it applies to both, depending on it's configuration");
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown fill mode", fillMode);
|
||||
return null;
|
||||
}
|
||||
|
||||
static resetUniqueDisplayNameNumbers()
|
||||
{
|
||||
WI.Animation._nextUniqueDisplayNameNumber = 1;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get animationId() { return this._animationId; }
|
||||
get name() { return this._name; }
|
||||
get cssAnimationName() { return this._cssAnimationName; }
|
||||
get cssTransitionProperty() { return this._cssTransitionProperty; }
|
||||
get stackTrace() { return this._stackTrace; }
|
||||
|
||||
get animationType()
|
||||
{
|
||||
if (this._cssAnimationName)
|
||||
return WI.Animation.Type.CSSAnimation;
|
||||
if (this._cssTransitionProperty)
|
||||
return WI.Animation.Type.CSSTransition;
|
||||
return WI.Animation.Type.WebAnimation;
|
||||
}
|
||||
|
||||
get startDelay()
|
||||
{
|
||||
return "startDelay" in this._effect ? this._effect.startDelay : NaN;
|
||||
}
|
||||
|
||||
get endDelay()
|
||||
{
|
||||
return "endDelay" in this._effect ? this._effect.endDelay : NaN;
|
||||
}
|
||||
|
||||
get iterationCount()
|
||||
{
|
||||
return "iterationCount" in this._effect ? this._effect.iterationCount : NaN;
|
||||
}
|
||||
|
||||
get iterationStart()
|
||||
{
|
||||
return "iterationStart" in this._effect ? this._effect.iterationStart : NaN;
|
||||
}
|
||||
|
||||
get iterationDuration()
|
||||
{
|
||||
return "iterationDuration" in this._effect ? this._effect.iterationDuration : NaN;
|
||||
}
|
||||
|
||||
get timingFunction()
|
||||
{
|
||||
return "timingFunction" in this._effect ? this._effect.timingFunction : null;
|
||||
}
|
||||
|
||||
get playbackDirection()
|
||||
{
|
||||
return "playbackDirection" in this._effect ? this._effect.playbackDirection : null;
|
||||
}
|
||||
|
||||
get fillMode()
|
||||
{
|
||||
return "fillMode" in this._effect ? this._effect.fillMode : null;
|
||||
}
|
||||
|
||||
get keyframes()
|
||||
{
|
||||
return "keyframes" in this._effect ? this._effect.keyframes : [];
|
||||
}
|
||||
|
||||
get displayName()
|
||||
{
|
||||
if (this._name)
|
||||
return this._name;
|
||||
|
||||
if (this._cssAnimationName)
|
||||
return this._cssAnimationName;
|
||||
|
||||
if (this._cssTransitionProperty)
|
||||
return this._cssTransitionProperty;
|
||||
|
||||
if (!this._uniqueDisplayNameNumber)
|
||||
this._uniqueDisplayNameNumber = WI.Animation._nextUniqueDisplayNameNumber++;
|
||||
return WI.UIString("Animation %d").format(this._uniqueDisplayNameNumber);
|
||||
}
|
||||
|
||||
requestEffectTarget(callback)
|
||||
{
|
||||
if (this._effectTarget !== undefined) {
|
||||
callback(this._effectTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._requestEffectTargetCallbacks) {
|
||||
this._requestEffectTargetCallbacks.push(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
this._requestEffectTargetCallbacks = [callback];
|
||||
|
||||
WI.domManager.ensureDocument();
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
target.AnimationAgent.requestEffectTarget(this._animationId, (error, effectTarget) => {
|
||||
// COMPATIBILITY (macOS 12.3, iOS 15.4): nodeId was renamed to effectTarget and changed from DOM.NodeId to DOM.Styleable.
|
||||
if (!isNaN(effectTarget))
|
||||
effectTarget = {nodeId: effectTarget};
|
||||
|
||||
this._effectTarget = !error ? WI.DOMStyleable.fromPayload(effectTarget) : null;
|
||||
|
||||
for (let requestEffectTargetCallback of this._requestEffectTargetCallbacks)
|
||||
requestEffectTargetCallback(this._effectTarget);
|
||||
|
||||
this._requestEffectTargetCallbacks = null;
|
||||
});
|
||||
}
|
||||
|
||||
// AnimationManager
|
||||
|
||||
nameChanged(name)
|
||||
{
|
||||
this._name = name || null;
|
||||
|
||||
this.dispatchEventToListeners(WI.Animation.Event.NameChanged);
|
||||
}
|
||||
|
||||
effectChanged(effect)
|
||||
{
|
||||
this._updateEffect(effect);
|
||||
}
|
||||
|
||||
targetChanged()
|
||||
{
|
||||
this._effectTarget = undefined;
|
||||
|
||||
this.dispatchEventToListeners(WI.Animation.Event.TargetChanged);
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_updateEffect(effect)
|
||||
{
|
||||
this._effect = effect || {};
|
||||
|
||||
if ("iterationCount" in this._effect) {
|
||||
if (this._effect.iterationCount === -1)
|
||||
this._effect.iterationCount = Infinity;
|
||||
else if (this._effect.iterationCount === null) {
|
||||
// COMPATIBILITY (iOS 14): an iteration count of `Infinity` was not properly handled.
|
||||
this._effect.iterationCount = Infinity;
|
||||
}
|
||||
}
|
||||
|
||||
if ("timingFunction" in this._effect) {
|
||||
let timingFunction = this._effect.timingFunction;
|
||||
this._effect.timingFunction = WI.CubicBezier.fromString(timingFunction) || WI.StepsFunction.fromString(timingFunction) || WI.Spring.fromString(timingFunction);
|
||||
console.assert(this._effect.timingFunction, timingFunction);
|
||||
}
|
||||
|
||||
if ("keyframes" in this._effect) {
|
||||
for (let keyframe of this._effect.keyframes) {
|
||||
if (keyframe.easing) {
|
||||
let easing = keyframe.easing;
|
||||
keyframe.easing = WI.CubicBezier.fromString(easing) || WI.StepsFunction.fromString(easing) || WI.Spring.fromString(easing);
|
||||
console.assert(keyframe.easing, easing);
|
||||
}
|
||||
|
||||
if (keyframe.style)
|
||||
keyframe.style = keyframe.style.replaceAll(/;\s+/g, ";\n");
|
||||
}
|
||||
}
|
||||
|
||||
this.dispatchEventToListeners(WI.Animation.Event.EffectChanged);
|
||||
}
|
||||
};
|
||||
|
||||
WI.Animation._nextUniqueDisplayNameNumber = 1;
|
||||
|
||||
WI.Animation.Type = {
|
||||
WebAnimation: "web-animation",
|
||||
CSSAnimation: "css-animation",
|
||||
CSSTransition: "css-transition",
|
||||
};
|
||||
|
||||
WI.Animation.PlaybackDirection = {
|
||||
Normal: "normal",
|
||||
Reverse: "reverse",
|
||||
Alternate: "alternate",
|
||||
AlternateReverse: "alternate-reverse",
|
||||
};
|
||||
|
||||
WI.Animation.FillMode = {
|
||||
None: "none",
|
||||
Forwards: "forwards",
|
||||
Backwards: "backwards",
|
||||
Both: "both",
|
||||
Auto: "auto",
|
||||
};
|
||||
|
||||
WI.Animation.Event = {
|
||||
NameChanged: "animation-name-changed",
|
||||
EffectChanged: "animation-effect-changed",
|
||||
TargetChanged: "animation-target-changed",
|
||||
};
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.AnimationCollection = class AnimationCollection extends WI.Collection
|
||||
{
|
||||
constructor(animationType)
|
||||
{
|
||||
console.assert(!animationType || Object.values(WI.Animation.Type).includes(animationType));
|
||||
|
||||
super();
|
||||
|
||||
this._animationType = animationType || null;
|
||||
|
||||
if (!this._animationType)
|
||||
this._animationCollectionForTypeMap = null;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get animationType() { return this._animationType; }
|
||||
|
||||
get displayName()
|
||||
{
|
||||
if (this._animationType) {
|
||||
const plural = true;
|
||||
return WI.Animation.displayNameForType(this._animationType, plural);
|
||||
}
|
||||
|
||||
return WI.UIString("Web Animations");
|
||||
}
|
||||
|
||||
objectIsRequiredType(object)
|
||||
{
|
||||
if (!(object instanceof WI.Animation))
|
||||
return false;
|
||||
|
||||
return !this._animationType || object.animationType === this._animationType;
|
||||
}
|
||||
|
||||
animationCollectionForType(animationType)
|
||||
{
|
||||
console.assert(Object.values(WI.Animation.Type).includes(animationType));
|
||||
|
||||
if (this._animationType) {
|
||||
console.assert(animationType === this._animationType);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!this._animationCollectionForTypeMap)
|
||||
this._animationCollectionForTypeMap = new Map;
|
||||
|
||||
let animationCollectionForType = this._animationCollectionForTypeMap.get(animationType);
|
||||
if (!animationCollectionForType) {
|
||||
animationCollectionForType = new WI.AnimationCollection(animationType);
|
||||
this._animationCollectionForTypeMap.set(animationType, animationCollectionForType);
|
||||
}
|
||||
return animationCollectionForType;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
itemAdded(item)
|
||||
{
|
||||
super.itemAdded(item);
|
||||
|
||||
if (!this._animationType) {
|
||||
let animationCollectionForType = this.animationCollectionForType(item.animationType);
|
||||
animationCollectionForType.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
itemRemoved(item)
|
||||
{
|
||||
if (!this._animationType) {
|
||||
let animationCollectionForType = this.animationCollectionForType(item.animationType);
|
||||
animationCollectionForType.remove(item);
|
||||
}
|
||||
|
||||
super.itemRemoved(item);
|
||||
}
|
||||
|
||||
itemsCleared(items)
|
||||
{
|
||||
if (this._animationCollectionForTypeMap) {
|
||||
for (let animationCollectionForType of this._animationCollectionForTypeMap.values())
|
||||
animationCollectionForType.clear();
|
||||
}
|
||||
|
||||
super.itemsCleared(items);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.ApplicationCacheFrame = class ApplicationCacheFrame
|
||||
{
|
||||
constructor(frame, manifest, status)
|
||||
{
|
||||
console.assert(frame instanceof WI.Frame);
|
||||
console.assert(manifest instanceof WI.ApplicationCacheManifest);
|
||||
|
||||
this._frame = frame;
|
||||
this._manifest = manifest;
|
||||
this._status = status;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get frame() { return this._frame; }
|
||||
get manifest() { return this._manifest; }
|
||||
get status() { return this._status; }
|
||||
set status(status) { this._status = status; }
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie[WI.ApplicationCacheFrame.FrameURLCookieKey] = this.frame.url;
|
||||
cookie[WI.ApplicationCacheFrame.ManifestURLCookieKey] = this.manifest.manifestURL;
|
||||
}
|
||||
};
|
||||
|
||||
WI.ApplicationCacheFrame.TypeIdentifier = "application-cache-frame";
|
||||
WI.ApplicationCacheFrame.FrameURLCookieKey = "application-cache-frame-url";
|
||||
WI.ApplicationCacheFrame.ManifestURLCookieKey = "application-cache-frame-manifest-url";
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.ApplicationCacheManifest = class ApplicationCacheManifest
|
||||
{
|
||||
constructor(manifestURL)
|
||||
{
|
||||
this._manifestURL = manifestURL;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get manifestURL() { return this._manifestURL; }
|
||||
};
|
||||
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.AuditTestBase = class AuditTestBase extends WI.Object
|
||||
{
|
||||
constructor(name, {description, supports, setup, disabled} = {})
|
||||
{
|
||||
console.assert(typeof name === "string", name);
|
||||
console.assert(!description || typeof description === "string", description);
|
||||
console.assert(supports === undefined || typeof supports === "number", supports);
|
||||
console.assert(!setup || typeof setup === "string", setup);
|
||||
console.assert(disabled === undefined || typeof disabled === "boolean", disabled);
|
||||
|
||||
super();
|
||||
|
||||
// This class should not be instantiated directly. Create a concrete subclass instead.
|
||||
console.assert(this.constructor !== WI.AuditTestBase && this instanceof WI.AuditTestBase, this);
|
||||
|
||||
this._name = name;
|
||||
this._description = description || "";
|
||||
this._supports = supports ?? NaN;
|
||||
this._setup = setup || "";
|
||||
|
||||
this.determineIfSupported({warn: true});
|
||||
|
||||
this._runningState = disabled ? WI.AuditManager.RunningState.Disabled : WI.AuditManager.RunningState.Inactive;
|
||||
this._result = null;
|
||||
|
||||
this._parent = null;
|
||||
|
||||
this._default = false;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get runningState() { return this._runningState; }
|
||||
get result() { return this._result; }
|
||||
get supported() { return this._supported; }
|
||||
|
||||
get name()
|
||||
{
|
||||
return this._name;
|
||||
}
|
||||
|
||||
set name(name)
|
||||
{
|
||||
console.assert(this.editable);
|
||||
console.assert(WI.auditManager.editing);
|
||||
console.assert(name && typeof name === "string", name);
|
||||
|
||||
if (name === this._name)
|
||||
return;
|
||||
|
||||
let oldName = this._name;
|
||||
this._name = name;
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.NameChanged, {oldName});
|
||||
}
|
||||
|
||||
get description()
|
||||
{
|
||||
return this._description;
|
||||
}
|
||||
|
||||
set description(description)
|
||||
{
|
||||
console.assert(this.editable);
|
||||
console.assert(WI.auditManager.editing);
|
||||
console.assert(typeof description === "string", description);
|
||||
|
||||
if (description === this._description)
|
||||
return;
|
||||
|
||||
this._description = description;
|
||||
}
|
||||
|
||||
get supports()
|
||||
{
|
||||
return this._supports;
|
||||
}
|
||||
|
||||
set supports(supports)
|
||||
{
|
||||
console.assert(this.editable);
|
||||
console.assert(WI.auditManager.editing);
|
||||
console.assert(typeof supports === "number", supports);
|
||||
|
||||
if (supports === this._supports)
|
||||
return;
|
||||
|
||||
this._supports = supports;
|
||||
this.determineIfSupported();
|
||||
}
|
||||
|
||||
get setup()
|
||||
{
|
||||
return this._setup;
|
||||
}
|
||||
|
||||
set setup(setup)
|
||||
{
|
||||
console.assert(this.editable);
|
||||
console.assert(typeof setup === "string", setup);
|
||||
|
||||
if (setup === this._setup)
|
||||
return;
|
||||
|
||||
this._setup = setup;
|
||||
|
||||
this.clearResult();
|
||||
}
|
||||
|
||||
get disabled()
|
||||
{
|
||||
return this._runningState === WI.AuditManager.RunningState.Disabled;
|
||||
}
|
||||
|
||||
set disabled(disabled)
|
||||
{
|
||||
this.updateDisabled(disabled);
|
||||
}
|
||||
|
||||
get editable()
|
||||
{
|
||||
return !this._default;
|
||||
}
|
||||
|
||||
get default()
|
||||
{
|
||||
return this._default;
|
||||
}
|
||||
|
||||
markAsDefault()
|
||||
{
|
||||
console.assert(!this._default);
|
||||
this._default = true;
|
||||
}
|
||||
|
||||
get topLevelTest()
|
||||
{
|
||||
let test = this;
|
||||
while (test._parent)
|
||||
test = test._parent;
|
||||
return test;
|
||||
}
|
||||
|
||||
async runSetup()
|
||||
{
|
||||
console.assert(this.topLevelTest === this);
|
||||
|
||||
if (!this._setup)
|
||||
return;
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
|
||||
let agentCommandFunction = null;
|
||||
let agentCommandArguments = {};
|
||||
if (target.hasDomain("Audit")) {
|
||||
agentCommandFunction = target.AuditAgent.run;
|
||||
agentCommandArguments.test = this._setup;
|
||||
} else {
|
||||
agentCommandFunction = target.RuntimeAgent.evaluate;
|
||||
agentCommandArguments.expression = `(function() { "use strict"; return eval(\`(${this._setup.replace(/`/g, "\\`")})\`)(); })()`;
|
||||
agentCommandArguments.objectGroup = AuditTestBase.ObjectGroup;
|
||||
agentCommandArguments.doNotPauseOnExceptionsAndMuteConsole = true;
|
||||
}
|
||||
|
||||
try {
|
||||
let response = await agentCommandFunction.invoke(agentCommandArguments);
|
||||
|
||||
if (response.result.type === "object" && response.result.className === "Promise") {
|
||||
if (WI.RuntimeManager.supportsAwaitPromise())
|
||||
response = await target.RuntimeAgent.awaitPromise(response.result.objectId);
|
||||
else {
|
||||
response = null;
|
||||
WI.AuditManager.synthesizeError(WI.UIString("Async audits are not supported."));
|
||||
}
|
||||
}
|
||||
|
||||
if (response) {
|
||||
let remoteObject = WI.RemoteObject.fromPayload(response.result, WI.mainTarget);
|
||||
if (response.wasThrown || (remoteObject.type === "object" && remoteObject.subtype === "error"))
|
||||
WI.AuditManager.synthesizeError(remoteObject.description);
|
||||
}
|
||||
} catch (error) {
|
||||
WI.AuditManager.synthesizeError(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async start()
|
||||
{
|
||||
// Called from WI.AuditManager.
|
||||
|
||||
if (!this._supported || this.disabled)
|
||||
return;
|
||||
|
||||
console.assert(WI.auditManager.runningState === WI.AuditManager.RunningState.Active);
|
||||
|
||||
console.assert(this._runningState === WI.AuditManager.RunningState.Inactive);
|
||||
if (this._runningState !== WI.AuditManager.RunningState.Inactive)
|
||||
return;
|
||||
|
||||
this._runningState = WI.AuditManager.RunningState.Active;
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.Scheduled);
|
||||
|
||||
await this.run();
|
||||
|
||||
this._runningState = WI.AuditManager.RunningState.Inactive;
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.Completed);
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
// Called from WI.AuditManager.
|
||||
// Overridden by sub-classes if needed.
|
||||
|
||||
if (!this._supported || this.disabled)
|
||||
return;
|
||||
|
||||
console.assert(WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping);
|
||||
|
||||
if (this._runningState !== WI.AuditManager.RunningState.Active)
|
||||
return;
|
||||
|
||||
this._runningState = WI.AuditManager.RunningState.Stopping;
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.Stopping);
|
||||
}
|
||||
|
||||
clearResult(options = {})
|
||||
{
|
||||
// Overridden by sub-classes if needed.
|
||||
|
||||
if (!this._result)
|
||||
return false;
|
||||
|
||||
this._result = null;
|
||||
|
||||
if (!options.suppressResultChangedEvent)
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.ResultChanged);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async clone()
|
||||
{
|
||||
console.assert(WI.auditManager.editing);
|
||||
|
||||
return this.constructor.fromPayload(this.toJSON());
|
||||
}
|
||||
|
||||
remove()
|
||||
{
|
||||
console.assert(WI.auditManager.editing);
|
||||
|
||||
if (!this._parent || this._default) {
|
||||
WI.auditManager.removeTest(this);
|
||||
return;
|
||||
}
|
||||
|
||||
console.assert(this.editable);
|
||||
console.assert(this._parent instanceof WI.AuditTestGroup);
|
||||
this._parent.removeTest(this);
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
let path = [];
|
||||
let test = this;
|
||||
while (test) {
|
||||
path.push(test.name);
|
||||
test = test._parent;
|
||||
}
|
||||
path.reverse();
|
||||
|
||||
cookie["audit-path"] = path.join(",");
|
||||
}
|
||||
|
||||
toJSON(key)
|
||||
{
|
||||
// Overridden by sub-classes if needed.
|
||||
|
||||
let json = {
|
||||
type: this.constructor.TypeIdentifier,
|
||||
name: this._name,
|
||||
};
|
||||
if (this._description)
|
||||
json.description = this._description;
|
||||
if (!isNaN(this._supports))
|
||||
json.supports = Number.isFinite(this._supports) ? this._supports : WI.AuditTestBase.Version + 1;
|
||||
if (this._setup)
|
||||
json.setup = this._setup;
|
||||
if (key === WI.ObjectStore.toJSONSymbol)
|
||||
json.disabled = this.disabled;
|
||||
return json;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
async run()
|
||||
{
|
||||
throw WI.NotImplementedError.subclassMustOverride();
|
||||
}
|
||||
|
||||
determineIfSupported(options = {})
|
||||
{
|
||||
// Overridden by sub-classes if needed.
|
||||
|
||||
let supportedBefore = this._supported;
|
||||
|
||||
if (this._supports > WI.AuditTestBase.Version) {
|
||||
this.updateSupported(false, options);
|
||||
|
||||
if (options.warn && supportedBefore !== this._supported && Number.isFinite(this._supports))
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 is too new to run in this Web Inspector").format(this.name));
|
||||
} else if (InspectorBackend.hasDomain("Audit") && this._supports > InspectorBackend.getVersion("Audit")) {
|
||||
this.updateSupported(false, options);
|
||||
|
||||
if (options.warn && supportedBefore !== this._supported && Number.isFinite(this._supports))
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 is too new to run in the inspected page").format(this.name));
|
||||
} else
|
||||
this.updateSupported(true, options);
|
||||
|
||||
return this._supported;
|
||||
}
|
||||
|
||||
updateSupported(supported, options = {})
|
||||
{
|
||||
// Overridden by sub-classes if needed.
|
||||
|
||||
if (supported === this._supported)
|
||||
return;
|
||||
|
||||
this._supported = supported;
|
||||
|
||||
if (!options.silent)
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.SupportedChanged);
|
||||
|
||||
if (!this._supported)
|
||||
this.clearResult();
|
||||
}
|
||||
|
||||
updateDisabled(disabled, options = {})
|
||||
{
|
||||
// Overridden by sub-classes if needed.
|
||||
|
||||
console.assert(this._runningState === WI.AuditManager.RunningState.Disabled || this._runningState === WI.AuditManager.RunningState.Inactive);
|
||||
if (this._runningState !== WI.AuditManager.RunningState.Disabled && this._runningState !== WI.AuditManager.RunningState.Inactive)
|
||||
return;
|
||||
|
||||
let runningState = disabled ? WI.AuditManager.RunningState.Disabled : WI.AuditManager.RunningState.Inactive;
|
||||
if (runningState === this._runningState)
|
||||
return;
|
||||
|
||||
this._runningState = runningState;
|
||||
|
||||
if (!options.silent)
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.DisabledChanged);
|
||||
|
||||
if (this.disabled)
|
||||
this.clearResult();
|
||||
}
|
||||
|
||||
updateResult(result)
|
||||
{
|
||||
// Overridden by sub-classes if needed.
|
||||
|
||||
console.assert(result instanceof WI.AuditTestResultBase, result);
|
||||
|
||||
this._result = result;
|
||||
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.ResultChanged);
|
||||
}
|
||||
};
|
||||
|
||||
// Keep this in sync with Inspector::Protocol::Audit::VERSION.
|
||||
WI.AuditTestBase.Version = 4;
|
||||
|
||||
WI.AuditTestBase.ObjectGroup = "audit";
|
||||
|
||||
WI.AuditTestBase.Event = {
|
||||
Completed: "audit-test-base-completed",
|
||||
DisabledChanged: "audit-test-base-disabled-changed",
|
||||
NameChanged: "audit-test-base-name-changed",
|
||||
Progress: "audit-test-base-progress",
|
||||
ResultChanged: "audit-test-base-result-changed",
|
||||
Scheduled: "audit-test-base-scheduled",
|
||||
Stopping: "audit-test-base-stopping",
|
||||
SupportedChanged: "audit-test-base-supported-changed",
|
||||
TestChanged: "audit-test-base-test-changed",
|
||||
};
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.AuditTestCase = class AuditTestCase extends WI.AuditTestBase
|
||||
{
|
||||
constructor(name, test, options = {})
|
||||
{
|
||||
console.assert(typeof test === "string", test);
|
||||
|
||||
super(name, options);
|
||||
|
||||
this._test = test;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static async fromPayload(payload)
|
||||
{
|
||||
if (typeof payload !== "object" || payload === null)
|
||||
return null;
|
||||
|
||||
if (payload.type !== WI.AuditTestCase.TypeIdentifier)
|
||||
return null;
|
||||
|
||||
if (typeof payload.name !== "string") {
|
||||
WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof payload.test !== "string") {
|
||||
WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("test")));
|
||||
return null;
|
||||
}
|
||||
|
||||
let options = {};
|
||||
|
||||
if (typeof payload.description === "string")
|
||||
options.description = payload.description;
|
||||
else if ("description" in payload)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
|
||||
|
||||
if (typeof payload.supports === "number")
|
||||
options.supports = payload.supports;
|
||||
else if ("supports" in payload)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-number \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("supports")));
|
||||
|
||||
if (typeof payload.setup === "string")
|
||||
options.setup = payload.setup;
|
||||
else if ("setup" in payload)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("setup")));
|
||||
|
||||
if (typeof payload.disabled === "boolean")
|
||||
options.disabled = payload.disabled;
|
||||
|
||||
return new WI.AuditTestCase(payload.name, payload.test, options);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get test()
|
||||
{
|
||||
return this._test;
|
||||
}
|
||||
|
||||
set test(test)
|
||||
{
|
||||
console.assert(this.editable);
|
||||
console.assert(typeof test === "string", test);
|
||||
|
||||
if (test === this._test)
|
||||
return;
|
||||
|
||||
this._test = test;
|
||||
|
||||
this.clearResult();
|
||||
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.TestChanged);
|
||||
}
|
||||
|
||||
toJSON(key)
|
||||
{
|
||||
let json = super.toJSON(key);
|
||||
json.test = this._test;
|
||||
return json;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
async run()
|
||||
{
|
||||
const levelStrings = Object.values(WI.AuditTestCaseResult.Level);
|
||||
let level = null;
|
||||
let data = {};
|
||||
let metadata = {
|
||||
url: WI.networkManager.mainFrame.url,
|
||||
startTimestamp: null,
|
||||
endTimestamp: null,
|
||||
};
|
||||
let resolvedDOMNodes = null;
|
||||
|
||||
function setLevel(newLevel) {
|
||||
let newLevelIndex = levelStrings.indexOf(newLevel);
|
||||
if (newLevelIndex < 0) {
|
||||
addError(WI.UIString("Return string must be one of %s").format(JSON.stringify(levelStrings)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (newLevelIndex <= levelStrings.indexOf(level))
|
||||
return;
|
||||
|
||||
level = newLevel;
|
||||
}
|
||||
|
||||
function addError(value) {
|
||||
setLevel(WI.AuditTestCaseResult.Level.Error);
|
||||
|
||||
if (!data.errors)
|
||||
data.errors = [];
|
||||
|
||||
data.errors.push(value);
|
||||
}
|
||||
|
||||
async function parseResponse(response) {
|
||||
let remoteObject = WI.RemoteObject.fromPayload(response.result, WI.mainTarget);
|
||||
if (response.wasThrown || (remoteObject.type === "object" && remoteObject.subtype === "error")) {
|
||||
addError(remoteObject.description);
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteObject.type === "boolean") {
|
||||
setLevel(remoteObject.value ? WI.AuditTestCaseResult.Level.Pass : WI.AuditTestCaseResult.Level.Fail);
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteObject.type === "string") {
|
||||
setLevel(remoteObject.value.trim().toLowerCase());
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteObject.type !== "object" || remoteObject.subtype) {
|
||||
addError(WI.UIString("Return value is not an object, string, or boolean"));
|
||||
return;
|
||||
}
|
||||
|
||||
const options = {
|
||||
ownProperties: true,
|
||||
};
|
||||
|
||||
function checkResultProperty(key, value, type, subtype) {
|
||||
function addErrorForValueType(valueType) {
|
||||
let errorString = null;
|
||||
if (valueType === "object" || valueType === "array")
|
||||
errorString = WI.UIString("\u0022%s\u0022 must be an %s");
|
||||
else
|
||||
errorString = WI.UIString("\u0022%s\u0022 must be a %s");
|
||||
addError(errorString.format(key, valueType));
|
||||
}
|
||||
|
||||
if (value.subtype !== subtype) {
|
||||
addErrorForValueType(subtype);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value.type !== type) {
|
||||
addErrorForValueType(type);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type === "boolean" || type === "string")
|
||||
return value.value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
async function resultArrayForEach(key, value, callback) {
|
||||
let array = checkResultProperty(key, value, "object", "array");
|
||||
if (!array)
|
||||
return;
|
||||
|
||||
let arrayProperties = await new Promise((resolve, reject) => array.getPropertyDescriptors(resolve, options));
|
||||
for (let i = 0; i < array.size; ++i) {
|
||||
let arrayPropertyForIndex = arrayProperties.find((arrayProperty) => arrayProperty.name === String(i));
|
||||
if (arrayPropertyForIndex)
|
||||
await callback(arrayPropertyForIndex);
|
||||
}
|
||||
}
|
||||
|
||||
let properties = await new Promise((resolve, reject) => remoteObject.getPropertyDescriptors(resolve, options));
|
||||
for (let property of properties) {
|
||||
let key = property.name;
|
||||
if (key === "__proto__")
|
||||
continue;
|
||||
|
||||
let value = property.value;
|
||||
|
||||
switch (key) {
|
||||
case "level": {
|
||||
let levelString = checkResultProperty(key, value, "string");
|
||||
if (levelString)
|
||||
setLevel(levelString.trim().toLowerCase());
|
||||
break;
|
||||
}
|
||||
|
||||
case "pass":
|
||||
if (checkResultProperty(key, value, "boolean"))
|
||||
setLevel(WI.AuditTestCaseResult.Level.Pass);
|
||||
break;
|
||||
|
||||
case "warn":
|
||||
if (checkResultProperty(key, value, "boolean"))
|
||||
setLevel(WI.AuditTestCaseResult.Level.Warn);
|
||||
break;
|
||||
|
||||
case "fail":
|
||||
if (checkResultProperty(key, value, "boolean"))
|
||||
setLevel(WI.AuditTestCaseResult.Level.Fail);
|
||||
break;
|
||||
|
||||
case "error":
|
||||
if (checkResultProperty(key, value, "boolean"))
|
||||
setLevel(WI.AuditTestCaseResult.Level.Error);
|
||||
break;
|
||||
|
||||
case "unsupported":
|
||||
if (checkResultProperty(key, value, "boolean"))
|
||||
setLevel(WI.AuditTestCaseResult.Level.Unsupported);
|
||||
break;
|
||||
|
||||
case "domNodes":
|
||||
await resultArrayForEach(key, value, async (item) => {
|
||||
if (!item || !item.value || item.value.type !== "object" || item.value.subtype !== "node") {
|
||||
addError(WI.UIString("All items in \u0022%s\u0022 must be valid DOM nodes").format(WI.unlocalizedString("domNodes")));
|
||||
return;
|
||||
}
|
||||
|
||||
let domNodeId = await new Promise((resolve, reject) => item.value.pushNodeToFrontend(resolve));
|
||||
let domNode = WI.domManager.nodeForId(domNodeId);
|
||||
if (!domNode)
|
||||
return;
|
||||
|
||||
if (!data.domNodes)
|
||||
data.domNodes = [];
|
||||
data.domNodes.push(WI.cssPath(domNode, {full: true}));
|
||||
|
||||
if (!resolvedDOMNodes)
|
||||
resolvedDOMNodes = [];
|
||||
resolvedDOMNodes.push(domNode);
|
||||
});
|
||||
break;
|
||||
|
||||
case "domAttributes":
|
||||
await resultArrayForEach(key, value, (item) => {
|
||||
if (!item || !item.value || item.value.type !== "string" || !item.value.value.length) {
|
||||
addError(WI.UIString("All items in \u0022%s\u0022 must be non-empty strings").format(WI.unlocalizedString("domAttributes")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.domAttributes)
|
||||
data.domAttributes = [];
|
||||
data.domAttributes.push(item.value.value);
|
||||
});
|
||||
break;
|
||||
|
||||
case "errors":
|
||||
await resultArrayForEach(key, value, (item) => {
|
||||
if (!item || !item.value || item.value.type !== "object" || item.value.subtype !== "error") {
|
||||
addError(WI.UIString("All items in \u0022%s\u0022 must be error objects").format(WI.unlocalizedString("errors")));
|
||||
return;
|
||||
}
|
||||
|
||||
addError(item.value.description);
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
if (value.objectId) {
|
||||
try {
|
||||
function inspectedPage_stringify() {
|
||||
return JSON.stringify(this);
|
||||
}
|
||||
let stringifiedValue = await value.callFunction(inspectedPage_stringify);
|
||||
data[key] = JSON.parse(stringifiedValue.value);
|
||||
} catch {
|
||||
addError(WI.UIString("\u0022%s\u0022 is not JSON serializable").format(key));
|
||||
}
|
||||
} else
|
||||
data[key] = value.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
|
||||
let agentCommandFunction = null;
|
||||
let agentCommandArguments = {};
|
||||
if (target.hasDomain("Audit")) {
|
||||
agentCommandFunction = target.AuditAgent.run;
|
||||
agentCommandArguments.test = this._test;
|
||||
} else {
|
||||
agentCommandFunction = target.RuntimeAgent.evaluate;
|
||||
agentCommandArguments.expression = `(function() { "use strict"; return eval(\`(${this._test.replace(/`/g, "\\`")})\`)(); })()`;
|
||||
agentCommandArguments.objectGroup = WI.AuditTestCase.ObjectGroup;
|
||||
agentCommandArguments.doNotPauseOnExceptionsAndMuteConsole = true;
|
||||
}
|
||||
|
||||
try {
|
||||
metadata.startTimestamp = new Date;
|
||||
let response = await agentCommandFunction.invoke(agentCommandArguments);
|
||||
metadata.endTimestamp = new Date;
|
||||
|
||||
if (response.result.type === "object" && response.result.className === "Promise") {
|
||||
if (WI.RuntimeManager.supportsAwaitPromise()) {
|
||||
metadata.asyncTimestamp = metadata.endTimestamp;
|
||||
response = await target.RuntimeAgent.awaitPromise(response.result.objectId);
|
||||
metadata.endTimestamp = new Date;
|
||||
} else {
|
||||
response = null;
|
||||
addError(WI.UIString("Async audits are not supported."));
|
||||
setLevel(WI.AuditTestCaseResult.Level.Unsupported);
|
||||
}
|
||||
}
|
||||
|
||||
if (response)
|
||||
await parseResponse(response);
|
||||
} catch (error) {
|
||||
metadata.endTimestamp = new Date;
|
||||
addError(error.message);
|
||||
}
|
||||
|
||||
if (!level)
|
||||
addError(WI.UIString("Missing result level"));
|
||||
|
||||
let options = {
|
||||
description: this.description,
|
||||
metadata,
|
||||
};
|
||||
if (!isEmptyObject(data))
|
||||
options.data = data;
|
||||
if (resolvedDOMNodes)
|
||||
options.resolvedDOMNodes = resolvedDOMNodes;
|
||||
this.updateResult(new WI.AuditTestCaseResult(this.name, level, options));
|
||||
}
|
||||
};
|
||||
|
||||
WI.AuditTestCase.TypeIdentifier = "test-case";
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.AuditTestCaseResult = class AuditTestCaseResult extends WI.AuditTestResultBase
|
||||
{
|
||||
constructor(name, level, {description, data, metadata, resolvedDOMNodes} = {})
|
||||
{
|
||||
console.assert(Object.values(WI.AuditTestCaseResult.Level).includes(level));
|
||||
console.assert(!data || typeof data === "object");
|
||||
console.assert(!metadata || typeof metadata === "object");
|
||||
|
||||
super(name, {description});
|
||||
|
||||
this._level = level;
|
||||
this._data = data || {};
|
||||
this._metadata = metadata || {};
|
||||
|
||||
// This array is a mirror of `this._data.domNodes` where each item is a `WI.DOMNode`.
|
||||
this._resolvedDOMNodes = resolvedDOMNodes || [];
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static async fromPayload(payload)
|
||||
{
|
||||
if (typeof payload !== "object" || payload === null)
|
||||
return null;
|
||||
|
||||
if (payload.type !== WI.AuditTestCaseResult.TypeIdentifier)
|
||||
return null;
|
||||
|
||||
if (typeof payload.name !== "string") {
|
||||
WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Object.values(WI.AuditTestCaseResult.Level).includes(payload.level)) {
|
||||
WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has an invalid \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("level")));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof payload.data !== "object" || payload.data === null) {
|
||||
if ("data" in payload)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("data")));
|
||||
payload.data = {};
|
||||
} else {
|
||||
function checkArray(key) {
|
||||
if (!(key in payload.data))
|
||||
return;
|
||||
|
||||
if (!Array.isArray(payload.data[key])) {
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-array \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("data.%s").format(key)));
|
||||
payload.data[key] = [];
|
||||
}
|
||||
|
||||
payload.data[key] = payload.data[key].filter((item) => typeof item === "string");
|
||||
}
|
||||
checkArray("domNodes");
|
||||
checkArray("domAttributes");
|
||||
checkArray("errors");
|
||||
}
|
||||
|
||||
if (typeof payload.metadata !== "object" || payload.metadata === null) {
|
||||
if ("metadata" in payload)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata")));
|
||||
|
||||
payload.metadata = {};
|
||||
} else {
|
||||
if (typeof payload.metadata.startTimestamp === "string")
|
||||
payload.metadata.startTimestamp = new Date(payload.metadata.startTimestamp);
|
||||
else {
|
||||
if ("startTimestamp" in payload.metadata)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.startTimestamp")));
|
||||
|
||||
payload.metadata.startTimestamp = null;
|
||||
}
|
||||
|
||||
if (typeof payload.metadata.asyncTimestamp === "string")
|
||||
payload.metadata.asyncTimestamp = new Date(payload.metadata.asyncTimestamp);
|
||||
else {
|
||||
if ("asyncTimestamp" in payload.metadata)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.asyncTimestamp")));
|
||||
|
||||
payload.metadata.asyncTimestamp = null;
|
||||
}
|
||||
|
||||
if (typeof payload.metadata.endTimestamp === "string")
|
||||
payload.metadata.endTimestamp = new Date(payload.metadata.endTimestamp);
|
||||
else {
|
||||
if ("endTimestamp" in payload.metadata)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.endTimestamp")));
|
||||
|
||||
payload.metadata.endTimestamp = null;
|
||||
}
|
||||
|
||||
if (typeof payload.metadata.url !== "string") {
|
||||
if ("url" in payload.metadata)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.url")));
|
||||
|
||||
payload.metadata.url = null;
|
||||
}
|
||||
}
|
||||
|
||||
let options = {};
|
||||
|
||||
if (typeof payload.description === "string")
|
||||
options.description = payload.description;
|
||||
else if ("description" in payload)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
|
||||
|
||||
if (!isEmptyObject(payload.data)) {
|
||||
options.data = {};
|
||||
for (let key in payload.data) {
|
||||
if (key === "domNodes" || key === "domAttributes" || key === "errors") {
|
||||
if (!payload.data[key].length)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key === "domNodes") {
|
||||
if (InspectorBackend.hasDomain("DOM") && (!payload.metadata.url || payload.metadata.url === WI.networkManager.mainFrame.url)) {
|
||||
let documentNode = await new Promise((resolve) => WI.domManager.requestDocument(resolve));
|
||||
options.resolvedDOMNodes = await Promise.all(payload.data.domNodes.map(async (domNodeString) => {
|
||||
let nodeId = 0;
|
||||
try {
|
||||
nodeId = await documentNode.querySelector(domNodeString);
|
||||
} catch { }
|
||||
return WI.domManager.nodeForId(nodeId);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
options.data[key] = payload.data[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEmptyObject(payload.metadata)) {
|
||||
options.metadata = {};
|
||||
if (payload.metadata.startTimestamp && !isNaN(payload.metadata.startTimestamp))
|
||||
options.metadata.startTimestamp = payload.metadata.startTimestamp;
|
||||
if (payload.metadata.asyncTimestamp && !isNaN(payload.metadata.asyncTimestamp))
|
||||
options.metadata.asyncTimestamp = payload.metadata.asyncTimestamp;
|
||||
if (payload.metadata.endTimestamp && !isNaN(payload.metadata.endTimestamp))
|
||||
options.metadata.endTimestamp = payload.metadata.endTimestamp;
|
||||
if (payload.metadata.url)
|
||||
options.metadata.url = payload.metadata.url;
|
||||
}
|
||||
|
||||
return new WI.AuditTestCaseResult(payload.name, payload.level, options);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get level() { return this._level; }
|
||||
get data() { return this._data; }
|
||||
get metadata() { return this._metadata; }
|
||||
get resolvedDOMNodes() { return this._resolvedDOMNodes; }
|
||||
|
||||
get result()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
get didPass()
|
||||
{
|
||||
return this._level === WI.AuditTestCaseResult.Level.Pass;
|
||||
}
|
||||
|
||||
get didWarn()
|
||||
{
|
||||
return this._level === WI.AuditTestCaseResult.Level.Warn;
|
||||
}
|
||||
|
||||
get didFail()
|
||||
{
|
||||
return this._level === WI.AuditTestCaseResult.Level.Fail;
|
||||
}
|
||||
|
||||
get didError()
|
||||
{
|
||||
return this._level === WI.AuditTestCaseResult.Level.Error;
|
||||
}
|
||||
|
||||
get unsupported()
|
||||
{
|
||||
return this._level === WI.AuditTestCaseResult.Level.Unsupported;
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
let json = super.toJSON();
|
||||
json.level = this._level;
|
||||
|
||||
let data = {};
|
||||
for (let key in this._data) {
|
||||
if (key === "domNodes" || key === "domAttributes" || key === "errors") {
|
||||
if (!this._data[key].length)
|
||||
continue;
|
||||
}
|
||||
|
||||
data[key] = this._data[key];
|
||||
}
|
||||
if (!isEmptyObject(data))
|
||||
json.data = data;
|
||||
|
||||
let metadata = {};
|
||||
if (this._metadata.startTimestamp && !isNaN(this._metadata.startTimestamp))
|
||||
metadata.startTimestamp = this._metadata.startTimestamp;
|
||||
if (this._metadata.asyncTimestamp && !isNaN(this._metadata.asyncTimestamp))
|
||||
metadata.asyncTimestamp = this._metadata.asyncTimestamp;
|
||||
if (this._metadata.endTimestamp && !isNaN(this._metadata.endTimestamp))
|
||||
metadata.endTimestamp = this._metadata.endTimestamp;
|
||||
if (this._metadata.url)
|
||||
metadata.url = this._metadata.url;
|
||||
if (!isEmptyObject(metadata))
|
||||
json.metadata = metadata;
|
||||
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
WI.AuditTestCaseResult.TypeIdentifier = "test-case-result";
|
||||
|
||||
// Keep this ordered by precedence.
|
||||
WI.AuditTestCaseResult.Level = {
|
||||
Pass: "pass",
|
||||
Warn: "warn",
|
||||
Fail: "fail",
|
||||
Error: "error",
|
||||
Unsupported: "unsupported",
|
||||
};
|
||||
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.AuditTestGroup = class AuditTestGroup extends WI.AuditTestBase
|
||||
{
|
||||
constructor(name, tests, options = {})
|
||||
{
|
||||
console.assert(Array.isArray(tests), tests);
|
||||
|
||||
// Set disabled once `_tests` is set so that it propagates.
|
||||
let disabled = options.disabled;
|
||||
options.disabled = false;
|
||||
|
||||
super(name, options);
|
||||
|
||||
this._tests = [];
|
||||
for (let test of tests)
|
||||
this.addTest(test);
|
||||
|
||||
if (disabled)
|
||||
this.updateDisabled(true);
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static async fromPayload(payload)
|
||||
{
|
||||
if (typeof payload !== "object" || payload === null)
|
||||
return null;
|
||||
|
||||
if (payload.type !== WI.AuditTestGroup.TypeIdentifier)
|
||||
return null;
|
||||
|
||||
if (typeof payload.name !== "string") {
|
||||
WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Array.isArray(payload.tests)) {
|
||||
WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-array \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("tests")));
|
||||
return null;
|
||||
}
|
||||
|
||||
let tests = await Promise.all(payload.tests.map(async (test) => {
|
||||
let testCase = await WI.AuditTestCase.fromPayload(test);
|
||||
if (testCase)
|
||||
return testCase;
|
||||
|
||||
let testGroup = await WI.AuditTestGroup.fromPayload(test);
|
||||
if (testGroup)
|
||||
return testGroup;
|
||||
|
||||
return null;
|
||||
}));
|
||||
tests = tests.filter((test) => !!test);
|
||||
if (!tests.length)
|
||||
return null;
|
||||
|
||||
let options = {};
|
||||
|
||||
if (typeof payload.description === "string")
|
||||
options.description = payload.description;
|
||||
else if ("description" in payload)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
|
||||
|
||||
if (typeof payload.supports === "number")
|
||||
options.supports = payload.supports;
|
||||
else if ("supports" in payload)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-number \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("supports")));
|
||||
|
||||
if (typeof payload.setup === "string")
|
||||
options.setup = payload.setup;
|
||||
else if ("setup" in payload)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("setup")));
|
||||
|
||||
if (typeof payload.disabled === "boolean")
|
||||
options.disabled = payload.disabled;
|
||||
|
||||
return new WI.AuditTestGroup(payload.name, tests, options);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get tests() { return this._tests; }
|
||||
|
||||
addTest(test)
|
||||
{
|
||||
console.assert(test instanceof WI.AuditTestBase, test);
|
||||
console.assert(!this._tests.includes(test), test);
|
||||
console.assert(!test._parent, test);
|
||||
|
||||
this._tests.push(test);
|
||||
test._parent = this;
|
||||
|
||||
test.addEventListener(WI.AuditTestBase.Event.Completed, this._handleTestCompleted, this);
|
||||
test.addEventListener(WI.AuditTestBase.Event.DisabledChanged, this._handleTestDisabledChanged, this);
|
||||
test.addEventListener(WI.AuditTestBase.Event.Progress, this._handleTestProgress, this);
|
||||
if (this.editable) {
|
||||
test.addEventListener(WI.AuditTestBase.Event.SupportedChanged, this._handleTestSupportedChanged, this);
|
||||
test.addEventListener(WI.AuditTestBase.Event.TestChanged, this._handleTestChanged, this);
|
||||
}
|
||||
|
||||
this.dispatchEventToListeners(WI.AuditTestGroup.Event.TestAdded, {test});
|
||||
|
||||
this.determineIfSupported();
|
||||
|
||||
if (this._checkDisabled(test))
|
||||
test.updateDisabled(true, {silent: true});
|
||||
}
|
||||
|
||||
removeTest(test)
|
||||
{
|
||||
console.assert(this.editable);
|
||||
console.assert(WI.auditManager.editing);
|
||||
console.assert(test instanceof WI.AuditTestBase, test);
|
||||
console.assert(test.editable, test);
|
||||
console.assert(this._tests.includes(test), test);
|
||||
console.assert(test._parent === this, test);
|
||||
|
||||
test.removeEventListener(WI.AuditTestBase.Event.Completed, this._handleTestCompleted, this);
|
||||
test.removeEventListener(WI.AuditTestBase.Event.DisabledChanged, this._handleTestDisabledChanged, this);
|
||||
test.removeEventListener(WI.AuditTestBase.Event.Progress, this._handleTestProgress, this);
|
||||
test.removeEventListener(WI.AuditTestBase.Event.SupportedChanged, this._handleTestSupportedChanged, this);
|
||||
test.removeEventListener(WI.AuditTestBase.Event.TestChanged, this._handleTestChanged, this);
|
||||
|
||||
this._tests.remove(test);
|
||||
test._parent = null;
|
||||
|
||||
this.dispatchEventToListeners(WI.AuditTestGroup.Event.TestRemoved, {test});
|
||||
|
||||
this.determineIfSupported();
|
||||
|
||||
this._checkDisabled();
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
// Called from WI.AuditManager.
|
||||
|
||||
for (let test of this._tests)
|
||||
test.stop();
|
||||
|
||||
super.stop();
|
||||
}
|
||||
|
||||
clearResult(options = {})
|
||||
{
|
||||
let cleared = !!this.result;
|
||||
|
||||
if (!options.excludeTests && this._tests) {
|
||||
for (let test of this._tests) {
|
||||
if (test.clearResult(options))
|
||||
cleared = true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.clearResult({
|
||||
...options,
|
||||
suppressResultChangedEvent: !cleared,
|
||||
});
|
||||
}
|
||||
|
||||
toJSON(key)
|
||||
{
|
||||
let json = super.toJSON(key);
|
||||
json.tests = this._tests.map((testCase) => testCase.toJSON(key));
|
||||
return json;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
async run()
|
||||
{
|
||||
let count = this._tests.length;
|
||||
for (let index = 0; index < count && this._runningState === WI.AuditManager.RunningState.Active; ++index) {
|
||||
let test = this._tests[index];
|
||||
if (test.disabled || !test.supported)
|
||||
continue;
|
||||
|
||||
await test.start();
|
||||
|
||||
if (test instanceof WI.AuditTestCase)
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.Progress, {index, count});
|
||||
}
|
||||
|
||||
this.updateResult();
|
||||
}
|
||||
|
||||
determineIfSupported(options = {})
|
||||
{
|
||||
if (this._tests) {
|
||||
for (let test of this._tests)
|
||||
test.determineIfSupported({...options, warn: false, silent: true});
|
||||
}
|
||||
|
||||
return super.determineIfSupported(options);
|
||||
}
|
||||
|
||||
updateSupported(supported, options = {})
|
||||
{
|
||||
if (this._tests && (!supported || this._tests.every((test) => !test.supported))) {
|
||||
supported = false;
|
||||
|
||||
for (let test of this._tests)
|
||||
test.updateSupported(supported, {silent: true});
|
||||
}
|
||||
|
||||
super.updateSupported(supported, options);
|
||||
}
|
||||
|
||||
updateDisabled(disabled, options = {})
|
||||
{
|
||||
if (!options.excludeTests && this._tests) {
|
||||
for (let test of this._tests)
|
||||
test.updateDisabled(disabled, options);
|
||||
}
|
||||
|
||||
super.updateDisabled(disabled, options);
|
||||
}
|
||||
|
||||
updateResult()
|
||||
{
|
||||
let results = this._tests.map((test) => test.result).filter((result) => !!result);
|
||||
if (!results.length)
|
||||
return;
|
||||
|
||||
super.updateResult(new WI.AuditTestGroupResult(this.name, results, {
|
||||
description: this.description,
|
||||
}));
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_checkDisabled(test)
|
||||
{
|
||||
let testDisabled = !test || !test.supported || test.disabled;
|
||||
let enabledTestCount = this._tests.filter((existing) => existing.supported && !existing.disabled).length;
|
||||
|
||||
if (testDisabled && !enabledTestCount)
|
||||
this.updateDisabled(true);
|
||||
else if (!testDisabled && enabledTestCount === 1)
|
||||
this.updateDisabled(false, {excludeTests: true});
|
||||
else {
|
||||
// Don't change `disabled`, as we're currently in an "indeterminate" state.
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.DisabledChanged);
|
||||
}
|
||||
|
||||
return this.disabled;
|
||||
}
|
||||
|
||||
_handleTestCompleted(event)
|
||||
{
|
||||
if (this._runningState === WI.AuditManager.RunningState.Active)
|
||||
return;
|
||||
|
||||
this.updateResult();
|
||||
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.Completed);
|
||||
}
|
||||
|
||||
_handleTestDisabledChanged(event)
|
||||
{
|
||||
this._checkDisabled(event.target);
|
||||
}
|
||||
|
||||
_handleTestProgress(event)
|
||||
{
|
||||
if (this._runningState !== WI.AuditManager.RunningState.Active)
|
||||
return;
|
||||
|
||||
let walk = (tests) => {
|
||||
let count = 0;
|
||||
for (let test of tests) {
|
||||
if (test.disabled || !test.supported)
|
||||
continue;
|
||||
|
||||
if (test instanceof WI.AuditTestCase)
|
||||
++count;
|
||||
else if (test instanceof WI.AuditTestGroup)
|
||||
count += walk(test.tests);
|
||||
}
|
||||
return count;
|
||||
};
|
||||
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.Progress, {
|
||||
index: event.data.index + walk(this._tests.slice(0, this._tests.indexOf(event.target))),
|
||||
count: walk(this._tests),
|
||||
});
|
||||
}
|
||||
|
||||
_handleTestSupportedChanged(event)
|
||||
{
|
||||
this.determineIfSupported();
|
||||
}
|
||||
|
||||
_handleTestChanged(event)
|
||||
{
|
||||
console.assert(WI.auditManager.editing);
|
||||
|
||||
this.clearResult({excludeTests: true});
|
||||
|
||||
this.dispatchEventToListeners(WI.AuditTestBase.Event.TestChanged);
|
||||
}
|
||||
};
|
||||
|
||||
WI.AuditTestGroup.TypeIdentifier = "test-group";
|
||||
|
||||
WI.AuditTestGroup.Event = {
|
||||
TestAdded: "audit-test-group-test-added",
|
||||
TestRemoved: "audit-test-group-test-removed",
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.AuditTestGroupResult = class AuditTestGroupResult extends WI.AuditTestResultBase
|
||||
{
|
||||
constructor(name, results, {description} = {})
|
||||
{
|
||||
console.assert(Array.isArray(results));
|
||||
|
||||
super(name, {description});
|
||||
|
||||
this._results = results;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static async fromPayload(payload)
|
||||
{
|
||||
if (typeof payload !== "object" || payload === null)
|
||||
return null;
|
||||
|
||||
if (payload.type !== WI.AuditTestGroupResult.TypeIdentifier)
|
||||
return null;
|
||||
|
||||
if (typeof payload.name !== "string") {
|
||||
WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Array.isArray(payload.results)) {
|
||||
WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-array \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("results")));
|
||||
return null;
|
||||
}
|
||||
|
||||
let results = await Promise.all(payload.results.map(async (test) => {
|
||||
let testCaseResult = await WI.AuditTestCaseResult.fromPayload(test);
|
||||
if (testCaseResult)
|
||||
return testCaseResult;
|
||||
|
||||
let testGroupResult = await WI.AuditTestGroupResult.fromPayload(test);
|
||||
if (testGroupResult)
|
||||
return testGroupResult;
|
||||
|
||||
return null;
|
||||
}));
|
||||
results = results.filter((result) => !!result);
|
||||
if (!results.length)
|
||||
return null;
|
||||
|
||||
let options = {};
|
||||
|
||||
if (typeof payload.description === "string")
|
||||
options.description = payload.description;
|
||||
else if ("description" in payload)
|
||||
WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
|
||||
|
||||
return new WI.AuditTestGroupResult(payload.name, results, options);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get results() { return this._results; }
|
||||
|
||||
get levelCounts()
|
||||
{
|
||||
let counts = {};
|
||||
for (let level of Object.values(WI.AuditTestCaseResult.Level))
|
||||
counts[level] = 0;
|
||||
|
||||
for (let result of this._results) {
|
||||
if (result instanceof WI.AuditTestCaseResult)
|
||||
++counts[result.level];
|
||||
else if (result instanceof WI.AuditTestGroupResult) {
|
||||
for (let [level, count] of Object.entries(result.levelCounts))
|
||||
counts[level] += count;
|
||||
}
|
||||
}
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
get didPass()
|
||||
{
|
||||
return this._results.some((result) => result.didPass);
|
||||
}
|
||||
|
||||
get didWarn()
|
||||
{
|
||||
return this._results.some((result) => result.didWarn);
|
||||
}
|
||||
|
||||
get didFail()
|
||||
{
|
||||
return this._results.some((result) => result.didFail);
|
||||
}
|
||||
|
||||
get didError()
|
||||
{
|
||||
return this._results.some((result) => result.didError);
|
||||
}
|
||||
|
||||
get unsupported()
|
||||
{
|
||||
return this._results.some((result) => result.unsupported);
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
let json = super.toJSON();
|
||||
json.results = this._results.map((result) => result.toJSON());
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
WI.AuditTestGroupResult.TypeIdentifier = "test-group-result";
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.AuditTestResultBase = class AuditTestResultBase
|
||||
{
|
||||
constructor(name, {description} = {})
|
||||
{
|
||||
console.assert(typeof name === "string");
|
||||
console.assert(!description || typeof description === "string");
|
||||
|
||||
this._name = name;
|
||||
this._description = description || null;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get name() { return this._name; }
|
||||
get description() { return this._description; }
|
||||
|
||||
get result()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
get didPass()
|
||||
{
|
||||
throw WI.NotImplementedError.subclassMustOverride();
|
||||
}
|
||||
|
||||
get didWarn()
|
||||
{
|
||||
throw WI.NotImplementedError.subclassMustOverride();
|
||||
}
|
||||
|
||||
get didFail()
|
||||
{
|
||||
throw WI.NotImplementedError.subclassMustOverride();
|
||||
}
|
||||
|
||||
get didError()
|
||||
{
|
||||
throw WI.NotImplementedError.subclassMustOverride();
|
||||
}
|
||||
|
||||
get unsupported()
|
||||
{
|
||||
throw WI.NotImplementedError.subclassMustOverride();
|
||||
}
|
||||
|
||||
get disabled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
get editable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie["audit-" + this.constructor.TypeIdentifier + "-name"] = this._name;
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
let json = {
|
||||
type: this.constructor.TypeIdentifier,
|
||||
name: this._name,
|
||||
};
|
||||
if (this._description)
|
||||
json.description = this._description;
|
||||
return json;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2013 University of Washington. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.BackForwardEntry = class BackForwardEntry
|
||||
{
|
||||
constructor(contentView, cookie)
|
||||
{
|
||||
this._contentView = contentView;
|
||||
|
||||
// ContentViews may be shared across multiple ContentViewContainers.
|
||||
// A BackForwardEntry containing a tombstone doesn't actually have
|
||||
// the real ContentView, and so should not close it.
|
||||
this._tombstone = false;
|
||||
|
||||
// Cookies are compared with Object.shallowEqual, so should not store objects or arrays.
|
||||
this._cookie = cookie || {};
|
||||
this._scrollPositions = [];
|
||||
|
||||
contentView.saveToCookie(this._cookie);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
makeCopy(newCookie)
|
||||
{
|
||||
let copy = new WI.BackForwardEntry(this._contentView, newCookie || this.cookie);
|
||||
copy._tombstone = this._tombstone;
|
||||
copy._scrollPositions = this._scrollPositions.slice();
|
||||
return copy;
|
||||
}
|
||||
|
||||
get contentView()
|
||||
{
|
||||
return this._contentView;
|
||||
}
|
||||
|
||||
get cookie()
|
||||
{
|
||||
// Cookies are immutable; they represent a specific navigation action.
|
||||
return Object.shallowCopy(this._cookie);
|
||||
}
|
||||
|
||||
get tombstone()
|
||||
{
|
||||
return this._tombstone;
|
||||
}
|
||||
|
||||
set tombstone(tombstone)
|
||||
{
|
||||
this._tombstone = tombstone;
|
||||
}
|
||||
|
||||
prepareToShow()
|
||||
{
|
||||
console.assert(!this._tombstone, "Should not be calling shown on a tombstone");
|
||||
|
||||
this._restoreFromCookie();
|
||||
|
||||
this.contentView.needsLayout();
|
||||
}
|
||||
|
||||
prepareToHide()
|
||||
{
|
||||
console.assert(!this._tombstone, "Should not be calling hidden on a tombstone");
|
||||
|
||||
this._saveScrollPositions();
|
||||
|
||||
if (this._contentView.shouldSaveStateWhenHidden) {
|
||||
this._cookie = {};
|
||||
this._contentView.saveToCookie(this._cookie);
|
||||
}
|
||||
}
|
||||
|
||||
isEqual(other)
|
||||
{
|
||||
if (!other)
|
||||
return false;
|
||||
return this._contentView === other._contentView && Object.shallowEqual(this._cookie, other._cookie);
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_restoreFromCookie()
|
||||
{
|
||||
this._restoreScrollPositions();
|
||||
this.contentView.restoreFromCookie(this.cookie);
|
||||
}
|
||||
|
||||
_restoreScrollPositions()
|
||||
{
|
||||
// If no scroll positions are saved, do nothing.
|
||||
if (!this._scrollPositions.length)
|
||||
return;
|
||||
|
||||
var scrollableElements = this.contentView.scrollableElements || [];
|
||||
console.assert(this._scrollPositions.length === scrollableElements.length);
|
||||
|
||||
for (var i = 0; i < scrollableElements.length; ++i) {
|
||||
var position = this._scrollPositions[i];
|
||||
var element = scrollableElements[i];
|
||||
if (!element)
|
||||
continue;
|
||||
|
||||
// Restore the top scroll position by either scrolling to the bottom or to the saved position.
|
||||
element.scrollTop = position.isScrolledToBottom ? element.scrollHeight : position.scrollTop;
|
||||
|
||||
// Don't restore the left scroll position when scrolled to the bottom. This way the when content changes
|
||||
// the user won't be left in a weird horizontal position.
|
||||
element.scrollLeft = position.isScrolledToBottom ? 0 : position.scrollLeft;
|
||||
}
|
||||
}
|
||||
|
||||
_saveScrollPositions()
|
||||
{
|
||||
var scrollableElements = this.contentView.scrollableElements || [];
|
||||
var scrollPositions = [];
|
||||
for (var i = 0; i < scrollableElements.length; ++i) {
|
||||
var element = scrollableElements[i];
|
||||
if (!element)
|
||||
continue;
|
||||
|
||||
let position = {scrollTop: element.scrollTop, scrollLeft: element.scrollLeft};
|
||||
if (this.contentView.shouldKeepElementsScrolledToBottom)
|
||||
position.isScrolledToBottom = element.isScrolledToBottom();
|
||||
|
||||
scrollPositions.push(position);
|
||||
}
|
||||
|
||||
this._scrollPositions = scrollPositions;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.BoxShadow = class BoxShadow
|
||||
{
|
||||
constructor(offsetX, offsetY, blurRadius, spreadRadius, inset, color)
|
||||
{
|
||||
console.assert(!offsetX || (typeof offsetX === "object" && !isNaN(offsetX.value) && offsetX.unit), offsetX);
|
||||
console.assert(!offsetY || (typeof offsetY === "object" && !isNaN(offsetY.value) && offsetY.unit), offsetY);
|
||||
console.assert(!blurRadius || (typeof blurRadius === "object" && blurRadius.value >= 0 && blurRadius.unit), blurRadius);
|
||||
console.assert(!spreadRadius || (typeof spreadRadius === "object" && !isNaN(spreadRadius.value) && spreadRadius.unit), spreadRadius);
|
||||
console.assert(!inset || typeof inset === "boolean", inset);
|
||||
console.assert(!color || color instanceof WI.Color, color);
|
||||
|
||||
this._offsetX = offsetX || null;
|
||||
this._offsetY = offsetY || null;
|
||||
this._blurRadius = blurRadius || null;
|
||||
this._spreadRadius = spreadRadius || null;
|
||||
this._inset = !!inset;
|
||||
this._color = color || null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromString(cssString)
|
||||
{
|
||||
if (cssString === "none")
|
||||
return new WI.BoxShadow;
|
||||
|
||||
let offsetX = null;
|
||||
let offsetY = null;
|
||||
let blurRadius = null;
|
||||
let spreadRadius = null;
|
||||
let inset = false;
|
||||
let color = null;
|
||||
|
||||
let startIndex = 0;
|
||||
let openParentheses = 0;
|
||||
let numberComponentCount = 0;
|
||||
for (let i = 0; i <= cssString.length; ++i) {
|
||||
if (cssString[i] === "(") {
|
||||
++openParentheses;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cssString[i] === ")") {
|
||||
--openParentheses;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((cssString[i] !== " " || openParentheses) && i !== cssString.length)
|
||||
continue;
|
||||
|
||||
let component = cssString.substring(startIndex, i + 1).trim();
|
||||
|
||||
startIndex = i + 1;
|
||||
|
||||
if (!component.length)
|
||||
continue;
|
||||
|
||||
if (component === "inset") {
|
||||
if (inset)
|
||||
return null;
|
||||
inset = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
let possibleColor = WI.Color.fromString(component);
|
||||
if (possibleColor) {
|
||||
if (color)
|
||||
return null;
|
||||
color = possibleColor;
|
||||
continue;
|
||||
}
|
||||
|
||||
let numberComponent = WI.BoxShadow.parseNumberComponent(component);
|
||||
if (!numberComponent)
|
||||
return null;
|
||||
|
||||
switch (++numberComponentCount) {
|
||||
case 1:
|
||||
offsetX = numberComponent;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
offsetY = numberComponent;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
blurRadius = numberComponent;
|
||||
if (blurRadius.value < 0)
|
||||
return null;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
spreadRadius = numberComponent;
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!offsetX || !offsetY)
|
||||
return null;
|
||||
|
||||
return new WI.BoxShadow(offsetX, offsetY, blurRadius, spreadRadius, inset, color);
|
||||
}
|
||||
|
||||
static parseNumberComponent(string)
|
||||
{
|
||||
let value = parseFloat(string);
|
||||
if (isNaN(value))
|
||||
return null;
|
||||
|
||||
let unit = string.replace(value, "");
|
||||
if (!unit) {
|
||||
if (value)
|
||||
return null;
|
||||
unit = "px";
|
||||
} else if (!WI.CSSCompletions.lengthUnits.has(unit))
|
||||
return null;
|
||||
|
||||
return {unit, value};
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get offsetX() { return this._offsetX; }
|
||||
get offsetY() { return this._offsetY; }
|
||||
get blurRadius() { return this._blurRadius; }
|
||||
get spreadRadius() { return this._spreadRadius; }
|
||||
get inset() { return this._inset; }
|
||||
get color() { return this._color; }
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.BoxShadow(this._offsetX, this._offsetY, this._blurRadius, this._spreadRadius, this._inset, this._color);
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
if (!this._offsetX || !this._offsetY)
|
||||
return "none";
|
||||
|
||||
function stringifyNumberComponent({value, unit}) {
|
||||
return value + (value ? unit : "");
|
||||
}
|
||||
|
||||
let values = [
|
||||
stringifyNumberComponent(this._offsetX),
|
||||
stringifyNumberComponent(this._offsetY),
|
||||
];
|
||||
|
||||
if (this._blurRadius)
|
||||
values.push(stringifyNumberComponent(this._blurRadius));
|
||||
|
||||
if (this._spreadRadius)
|
||||
values.push(stringifyNumberComponent(this._spreadRadius));
|
||||
|
||||
if (this._color)
|
||||
values.push(this._color.toString());
|
||||
|
||||
if (this._inset)
|
||||
values.push("inset");
|
||||
|
||||
return values.join(" ");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Branch = class Branch
|
||||
{
|
||||
constructor(displayName, revisions, locked)
|
||||
{
|
||||
console.assert(displayName);
|
||||
|
||||
this._displayName = displayName;
|
||||
this._revisions = revisions instanceof Array ? revisions.slice() : [];
|
||||
this._locked = locked || false;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return this._displayName;
|
||||
}
|
||||
|
||||
set displayName(displayName)
|
||||
{
|
||||
console.assert(displayName);
|
||||
if (!displayName)
|
||||
return;
|
||||
|
||||
this._displayName = displayName;
|
||||
}
|
||||
|
||||
get revisions()
|
||||
{
|
||||
return this._revisions;
|
||||
}
|
||||
|
||||
get locked()
|
||||
{
|
||||
return this._locked;
|
||||
}
|
||||
|
||||
revisionForRepresentedObject(representedObject, doNotCreateIfNeeded)
|
||||
{
|
||||
for (var i = 0; i < this._revisions.length; ++i) {
|
||||
var revision = this._revisions[i];
|
||||
if (revision instanceof WI.SourceCodeRevision && revision.sourceCode === representedObject)
|
||||
return revision;
|
||||
}
|
||||
|
||||
if (doNotCreateIfNeeded)
|
||||
return null;
|
||||
|
||||
if (representedObject instanceof WI.SourceCode) {
|
||||
var revision = representedObject.originalRevision.copy();
|
||||
representedObject.currentRevision = revision;
|
||||
this.addRevision(revision);
|
||||
return revision;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
addRevision(revision)
|
||||
{
|
||||
console.assert(revision instanceof WI.Revision);
|
||||
|
||||
if (this._locked)
|
||||
return;
|
||||
|
||||
if (this._revisions.includes(revision))
|
||||
return;
|
||||
|
||||
this._revisions.push(revision);
|
||||
}
|
||||
|
||||
removeRevision(revision)
|
||||
{
|
||||
console.assert(revision instanceof WI.Revision);
|
||||
|
||||
if (this._locked)
|
||||
return;
|
||||
|
||||
this._revisions.remove(revision);
|
||||
}
|
||||
|
||||
reset()
|
||||
{
|
||||
if (this._locked)
|
||||
return;
|
||||
|
||||
this._revisions = [];
|
||||
}
|
||||
|
||||
fork(displayName)
|
||||
{
|
||||
var copiedRevisions = this._revisions.map(function(revision) { return revision.copy(); });
|
||||
return new WI.Branch(displayName, copiedRevisions);
|
||||
}
|
||||
|
||||
apply()
|
||||
{
|
||||
for (var i = 0; i < this._revisions.length; ++i)
|
||||
this._revisions[i].apply();
|
||||
}
|
||||
|
||||
revert()
|
||||
{
|
||||
for (var i = this._revisions.length - 1; i >= 0; --i)
|
||||
this._revisions[i].revert();
|
||||
}
|
||||
|
||||
lock()
|
||||
{
|
||||
console.assert(!this._locked);
|
||||
this._locked = true;
|
||||
}
|
||||
|
||||
unlock()
|
||||
{
|
||||
console.assert(this._locked);
|
||||
this._locked = false;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Breakpoint = class Breakpoint extends WI.Object
|
||||
{
|
||||
constructor({disabled, condition, actions, ignoreCount, autoContinue} = {})
|
||||
{
|
||||
console.assert(!disabled || typeof disabled === "boolean", disabled);
|
||||
console.assert(!condition || typeof condition === "string", condition);
|
||||
console.assert(!actions || Array.isArray(actions), actions);
|
||||
console.assert(!ignoreCount || !isNaN(ignoreCount), ignoreCount);
|
||||
console.assert(!autoContinue || typeof autoContinue === "boolean", autoContinue);
|
||||
|
||||
super();
|
||||
|
||||
// This class should not be instantiated directly. Create a concrete subclass instead.
|
||||
console.assert(this.constructor !== WI.Breakpoint && this instanceof WI.Breakpoint);
|
||||
console.assert(this.constructor.ReferencePage, "Should have a link to a reference page.");
|
||||
|
||||
this._disabled = disabled || false;
|
||||
this._condition = condition || "";
|
||||
this._ignoreCount = ignoreCount || 0;
|
||||
this._autoContinue = autoContinue || false;
|
||||
this._actions = actions || [];
|
||||
|
||||
for (let action of this._actions)
|
||||
action.addEventListener(WI.BreakpointAction.Event.Modified, this._handleBreakpointActionModified, this);
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
toJSON(key)
|
||||
{
|
||||
let json = {};
|
||||
if (this._disabled)
|
||||
json.disabled = this._disabled;
|
||||
if (this.editable) {
|
||||
if (this._condition)
|
||||
json.condition = this._condition;
|
||||
if (this._ignoreCount)
|
||||
json.ignoreCount = this._ignoreCount;
|
||||
if (this._actions.length)
|
||||
json.actions = this._actions.map((action) => action.toJSON());
|
||||
if (this._autoContinue)
|
||||
json.autoContinue = this._autoContinue;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get displayName()
|
||||
{
|
||||
throw WI.NotImplementedError.subclassMustOverride();
|
||||
}
|
||||
|
||||
get special()
|
||||
{
|
||||
// Overridden by subclasses if needed.
|
||||
return false;
|
||||
}
|
||||
|
||||
get removable()
|
||||
{
|
||||
// Overridden by subclasses if needed.
|
||||
return true;
|
||||
}
|
||||
|
||||
get editable()
|
||||
{
|
||||
// Overridden by subclasses if needed.
|
||||
return false;
|
||||
}
|
||||
|
||||
get resolved()
|
||||
{
|
||||
// Overridden by subclasses if needed.
|
||||
return WI.debuggerManager.breakpointsEnabled;
|
||||
}
|
||||
|
||||
get disabled()
|
||||
{
|
||||
return this._disabled;
|
||||
}
|
||||
|
||||
set disabled(disabled)
|
||||
{
|
||||
if (this._disabled === disabled)
|
||||
return;
|
||||
|
||||
this._disabled = disabled || false;
|
||||
|
||||
this.dispatchEventToListeners(WI.Breakpoint.Event.DisabledStateDidChange);
|
||||
}
|
||||
|
||||
get condition()
|
||||
{
|
||||
return this._condition;
|
||||
}
|
||||
|
||||
set condition(condition)
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
console.assert(typeof condition === "string");
|
||||
|
||||
if (this._condition === condition)
|
||||
return;
|
||||
|
||||
this._condition = condition;
|
||||
|
||||
this.dispatchEventToListeners(WI.Breakpoint.Event.ConditionDidChange);
|
||||
}
|
||||
|
||||
get ignoreCount()
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
|
||||
return this._ignoreCount;
|
||||
}
|
||||
|
||||
set ignoreCount(ignoreCount)
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
|
||||
console.assert(ignoreCount >= 0, "Ignore count cannot be negative.");
|
||||
if (ignoreCount < 0)
|
||||
return;
|
||||
|
||||
if (this._ignoreCount === ignoreCount)
|
||||
return;
|
||||
|
||||
this._ignoreCount = ignoreCount;
|
||||
|
||||
this.dispatchEventToListeners(WI.Breakpoint.Event.IgnoreCountDidChange);
|
||||
}
|
||||
|
||||
get autoContinue()
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
|
||||
return this._autoContinue;
|
||||
}
|
||||
|
||||
set autoContinue(cont)
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
|
||||
if (this._autoContinue === cont)
|
||||
return;
|
||||
|
||||
this._autoContinue = cont;
|
||||
|
||||
this.dispatchEventToListeners(WI.Breakpoint.Event.AutoContinueDidChange);
|
||||
}
|
||||
|
||||
get actions()
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
|
||||
return this._actions;
|
||||
}
|
||||
|
||||
get probeActions()
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
|
||||
return this._actions.filter(function(action) {
|
||||
return action.type === WI.BreakpointAction.Type.Probe;
|
||||
});
|
||||
}
|
||||
|
||||
addAction(action, {precedingAction} = {})
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
console.assert(action instanceof WI.BreakpointAction, action);
|
||||
|
||||
action.addEventListener(WI.BreakpointAction.Event.Modified, this._handleBreakpointActionModified, this);
|
||||
|
||||
if (!precedingAction)
|
||||
this._actions.push(action);
|
||||
else {
|
||||
var index = this._actions.indexOf(precedingAction);
|
||||
console.assert(index !== -1);
|
||||
if (index === -1)
|
||||
this._actions.push(action);
|
||||
else
|
||||
this._actions.splice(index + 1, 0, action);
|
||||
}
|
||||
|
||||
this.dispatchEventToListeners(WI.Breakpoint.Event.ActionsDidChange);
|
||||
}
|
||||
|
||||
removeAction(action)
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
console.assert(action instanceof WI.BreakpointAction, action);
|
||||
|
||||
var index = this._actions.indexOf(action);
|
||||
console.assert(index !== -1);
|
||||
if (index === -1)
|
||||
return;
|
||||
|
||||
this._actions.splice(index, 1);
|
||||
|
||||
action.removeEventListener(WI.BreakpointAction.Event.Modified, this._handleBreakpointActionModified, this);
|
||||
|
||||
if (!this._actions.length)
|
||||
this.autoContinue = false;
|
||||
|
||||
this.dispatchEventToListeners(WI.Breakpoint.Event.ActionsDidChange);
|
||||
}
|
||||
|
||||
clearActions(type)
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
|
||||
if (!type)
|
||||
this._actions = [];
|
||||
else
|
||||
this._actions = this._actions.filter(function(action) { return action.type !== type; });
|
||||
|
||||
this.dispatchEventToListeners(WI.Breakpoint.Event.ActionsDidChange);
|
||||
}
|
||||
|
||||
reset()
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
|
||||
this.condition = "";
|
||||
this.ignoreCount = 0;
|
||||
this.autoContinue = false;
|
||||
this.clearActions();
|
||||
}
|
||||
|
||||
remove()
|
||||
{
|
||||
console.assert(this.removable, this);
|
||||
|
||||
// Overridden by subclasses if needed.
|
||||
}
|
||||
|
||||
optionsToProtocol()
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
|
||||
let payload = {};
|
||||
|
||||
if (this._condition)
|
||||
payload.condition = this._condition;
|
||||
|
||||
if (this._actions.length) {
|
||||
payload.actions = this._actions.map((action) => action.toProtocol()).filter((action) => {
|
||||
if (action.type !== WI.BreakpointAction.Type.Log)
|
||||
return true;
|
||||
|
||||
if (!/\$\{.*?\}/.test(action.data))
|
||||
return true;
|
||||
|
||||
let lexer = new WI.BreakpointLogMessageLexer;
|
||||
let tokens = lexer.tokenize(action.data);
|
||||
if (!tokens)
|
||||
return false;
|
||||
|
||||
let templateLiteral = tokens.reduce((text, token) => {
|
||||
if (token.type === WI.BreakpointLogMessageLexer.TokenType.PlainText)
|
||||
return text + token.data.escapeCharacters("`\\");
|
||||
if (token.type === WI.BreakpointLogMessageLexer.TokenType.Expression)
|
||||
return text + "${" + token.data + "}";
|
||||
return text;
|
||||
}, "");
|
||||
|
||||
action.data = "console.log(`" + templateLiteral + "`)";
|
||||
action.type = WI.BreakpointAction.Type.Evaluate;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (this._autoContinue)
|
||||
payload.autoContinue = this._autoContinue;
|
||||
|
||||
if (this._ignoreCount)
|
||||
payload.ignoreCount = this._ignoreCount;
|
||||
|
||||
return !isEmptyObject(payload) ? payload : undefined;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_handleBreakpointActionModified(event)
|
||||
{
|
||||
console.assert(this.editable, this);
|
||||
|
||||
this.dispatchEventToListeners(WI.Breakpoint.Event.ActionsDidChange);
|
||||
}
|
||||
};
|
||||
|
||||
WI.Breakpoint.TypeIdentifier = "breakpoint";
|
||||
|
||||
WI.Breakpoint.Event = {
|
||||
DisabledStateDidChange: "breakpoint-disabled-state-did-change",
|
||||
ConditionDidChange: "breakpoint-condition-did-change",
|
||||
IgnoreCountDidChange: "breakpoint-ignore-count-did-change",
|
||||
ActionsDidChange: "breakpoint-actions-did-change",
|
||||
AutoContinueDidChange: "breakpoint-auto-continue-did-change",
|
||||
};
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.BreakpointAction = class BreakpointAction extends WI.Object
|
||||
{
|
||||
constructor(type, {data, emulateUserGesture} = {})
|
||||
{
|
||||
console.assert(Object.values(WI.BreakpointAction.Type).includes(type), type);
|
||||
console.assert(!data || typeof data === "string", data);
|
||||
|
||||
super();
|
||||
|
||||
this._type = type;
|
||||
this._data = data || null;
|
||||
this._id = WI.debuggerManager.nextBreakpointActionIdentifier();
|
||||
this._emulateUserGesture = !!emulateUserGesture;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static supportsEmulateUserAction()
|
||||
{
|
||||
// COMPATIBILITY (iOS 14): the `emulateUserGesture` property of `Debugger.BreakpointAction` did not exist yet.
|
||||
// Since support can't be tested directly, check for the `options` parameter of `Debugger.setPauseOnExceptions`.
|
||||
// FIXME: Use explicit version checking once <https://webkit.org/b/148680> is fixed.
|
||||
return WI.sharedApp.isWebDebuggable() && InspectorBackend.hasCommand("Debugger.setPauseOnExceptions", "options");
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static fromJSON(json)
|
||||
{
|
||||
return new WI.BreakpointAction(json.type, {
|
||||
data: json.data,
|
||||
emulateUserGesture: json.emulateUserGesture,
|
||||
});
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
let json = {
|
||||
type: this._type,
|
||||
};
|
||||
if (this._data)
|
||||
json.data = this._data;
|
||||
if (this._emulateUserGesture)
|
||||
json.emulateUserGesture = this._emulateUserGesture;
|
||||
return json;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get id() { return this._id; }
|
||||
|
||||
get type()
|
||||
{
|
||||
return this._type;
|
||||
}
|
||||
|
||||
set type(type)
|
||||
{
|
||||
console.assert(Object.values(WI.BreakpointAction.Type).includes(type), type);
|
||||
|
||||
if (type === this._type)
|
||||
return;
|
||||
|
||||
this._type = type;
|
||||
|
||||
this.dispatchEventToListeners(WI.BreakpointAction.Event.Modified);
|
||||
}
|
||||
|
||||
get data()
|
||||
{
|
||||
return this._data;
|
||||
}
|
||||
|
||||
set data(data)
|
||||
{
|
||||
console.assert(!data || typeof data === "string", data);
|
||||
|
||||
if (this._data === data)
|
||||
return;
|
||||
|
||||
this._data = data;
|
||||
|
||||
this.dispatchEventToListeners(WI.BreakpointAction.Event.Modified);
|
||||
}
|
||||
|
||||
get emulateUserGesture()
|
||||
{
|
||||
return this._emulateUserGesture;
|
||||
}
|
||||
|
||||
set emulateUserGesture(emulateUserGesture)
|
||||
{
|
||||
if (this._emulateUserGesture === emulateUserGesture)
|
||||
return;
|
||||
|
||||
this._emulateUserGesture = emulateUserGesture;
|
||||
|
||||
this.dispatchEventToListeners(WI.BreakpointAction.Event.Modified);
|
||||
}
|
||||
|
||||
toProtocol()
|
||||
{
|
||||
let json = this.toJSON();
|
||||
json.id = this._id;
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
WI.BreakpointAction.Type = {
|
||||
Log: "log",
|
||||
Evaluate: "evaluate",
|
||||
Sound: "sound",
|
||||
Probe: "probe"
|
||||
};
|
||||
|
||||
WI.BreakpointAction.Event = {
|
||||
Modified: "breakpoint-action-modified",
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CPUInstrument = class CPUInstrument extends WI.Instrument
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(WI.CPUInstrument.supported());
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static supported()
|
||||
{
|
||||
// COMPATIBILITY (iOS 12): CPUProfiler domain did not exist.
|
||||
return InspectorBackend.hasDomain("CPUProfiler");
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
get timelineRecordType()
|
||||
{
|
||||
return WI.TimelineRecord.Type.CPU;
|
||||
}
|
||||
|
||||
startInstrumentation(initiatedByBackend)
|
||||
{
|
||||
if (!initiatedByBackend) {
|
||||
let target = WI.assumingMainTarget();
|
||||
target.CPUProfilerAgent.startTracking();
|
||||
}
|
||||
}
|
||||
|
||||
stopInstrumentation(initiatedByBackend)
|
||||
{
|
||||
if (!initiatedByBackend) {
|
||||
let target = WI.assumingMainTarget();
|
||||
target.CPUProfilerAgent.stopTracking();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CPUTimeline = class CPUTimeline extends WI.Timeline
|
||||
{
|
||||
// Public
|
||||
|
||||
addRecord(record, options = {})
|
||||
{
|
||||
let lastRecord = this.records.lastValue;
|
||||
if (lastRecord) {
|
||||
let startTime = lastRecord.endTime;
|
||||
if (options.discontinuity)
|
||||
startTime = options.discontinuity.endTime;
|
||||
record.adjustStartTime(startTime);
|
||||
}
|
||||
|
||||
super.addRecord(record, options);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CPUTimelineRecord = class CPUTimelineRecord extends WI.TimelineRecord
|
||||
{
|
||||
constructor({timestamp, usage, threads})
|
||||
{
|
||||
super(WI.TimelineRecord.Type.CPU, timestamp - CPUTimelineRecord.samplingRatePerSecond, timestamp);
|
||||
|
||||
console.assert(typeof timestamp === "number");
|
||||
console.assert(typeof usage === "number");
|
||||
console.assert(usage >= 0);
|
||||
console.assert(threads === undefined || Array.isArray(threads));
|
||||
|
||||
this._timestamp = timestamp;
|
||||
this._usage = usage;
|
||||
this._threads = threads || [];
|
||||
|
||||
this._mainThreadUsage = 0;
|
||||
this._webkitThreadUsage = 0;
|
||||
this._workerThreadUsage = 0;
|
||||
this._unknownThreadUsage = 0;
|
||||
this._workersData = null;
|
||||
|
||||
for (let thread of this._threads) {
|
||||
if (thread.type === InspectorBackend.Enum.CPUProfiler.ThreadInfoType.Main) {
|
||||
console.assert(!this._mainThreadUsage, "There should only be one main thread.");
|
||||
this._mainThreadUsage += thread.usage;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (thread.type === InspectorBackend.Enum.CPUProfiler.ThreadInfoType.WebKit) {
|
||||
if (thread.targetId) {
|
||||
if (!this._workersData)
|
||||
this._workersData = [];
|
||||
this._workersData.push(thread);
|
||||
this._workerThreadUsage += thread.usage;
|
||||
continue;
|
||||
}
|
||||
|
||||
this._webkitThreadUsage += thread.usage;
|
||||
continue;
|
||||
}
|
||||
|
||||
this._unknownThreadUsage += thread.usage;
|
||||
}
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static get samplingRatePerSecond()
|
||||
{
|
||||
// 500ms. This matches the ResourceUsageThread sampling frequency in the backend.
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static async fromJSON(json)
|
||||
{
|
||||
return new WI.CPUTimelineRecord(json);
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return {
|
||||
type: this.type,
|
||||
timestamp: this._timestamp,
|
||||
usage: this._usage,
|
||||
threads: this._threads,
|
||||
};
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get timestamp() { return this._timestamp; }
|
||||
get usage() { return this._usage; }
|
||||
|
||||
get unadjustedStartTime() { return this._timestamp; }
|
||||
|
||||
get mainThreadUsage() { return this._mainThreadUsage; }
|
||||
get webkitThreadUsage() { return this._webkitThreadUsage; }
|
||||
get workerThreadUsage() { return this._workerThreadUsage; }
|
||||
get unknownThreadUsage() { return this._unknownThreadUsage; }
|
||||
get workersData() { return this._workersData; }
|
||||
|
||||
adjustStartTime(startTime)
|
||||
{
|
||||
console.assert(startTime < this._endTime);
|
||||
this._startTime = startTime;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Nikita Vasilyev. All rights reserved.
|
||||
* Copyright (C) 2010 Joseph Pecoraro. All rights reserved.
|
||||
* Copyright (C) 2010 Google Inc. All rights reserved.
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CSSCompletions = class CSSCompletions
|
||||
{
|
||||
constructor(values, {acceptEmptyPrefix} = {})
|
||||
{
|
||||
console.assert(Array.isArray(values), values);
|
||||
console.assert(!values.length || typeof values[0] === "string", "Expected an array of string values or an empty array", values);
|
||||
|
||||
this._values = values.slice();
|
||||
this._values.sort();
|
||||
this._acceptEmptyPrefix = !!acceptEmptyPrefix;
|
||||
this._queryController = null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static completeUnbalancedValue(value)
|
||||
{
|
||||
const State = {
|
||||
Data: 0,
|
||||
SingleQuoteString: 1,
|
||||
DoubleQuoteString: 2,
|
||||
Comment: 3
|
||||
};
|
||||
|
||||
let state = State.Data;
|
||||
let unclosedParenthesisCount = 0;
|
||||
let trailingBackslash = false;
|
||||
let length = value.length;
|
||||
|
||||
for (let i = 0; i < length; ++i) {
|
||||
switch (value[i]) {
|
||||
case "'":
|
||||
if (state === State.Data)
|
||||
state = State.SingleQuoteString;
|
||||
else if (state === State.SingleQuoteString)
|
||||
state = State.Data;
|
||||
break;
|
||||
|
||||
case "\"":
|
||||
if (state === State.Data)
|
||||
state = State.DoubleQuoteString;
|
||||
else if (state === State.DoubleQuoteString)
|
||||
state = State.Data;
|
||||
break;
|
||||
|
||||
case "(":
|
||||
if (state === State.Data)
|
||||
++unclosedParenthesisCount;
|
||||
break;
|
||||
|
||||
case ")":
|
||||
if (state === State.Data && unclosedParenthesisCount)
|
||||
--unclosedParenthesisCount;
|
||||
break;
|
||||
|
||||
case "/":
|
||||
if (state === State.Data) {
|
||||
if (value[i + 1] === "*")
|
||||
state = State.Comment;
|
||||
}
|
||||
break;
|
||||
|
||||
case "\\":
|
||||
if (i === length - 1)
|
||||
trailingBackslash = true;
|
||||
else
|
||||
++i; // Skip next character.
|
||||
break;
|
||||
|
||||
case "*":
|
||||
if (state === State.Comment) {
|
||||
if (value[i + 1] === "/")
|
||||
state = State.Data;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let suffix = "";
|
||||
|
||||
if (trailingBackslash)
|
||||
suffix += "\\";
|
||||
|
||||
switch (state) {
|
||||
case State.SingleQuoteString:
|
||||
suffix += "'";
|
||||
break;
|
||||
case State.DoubleQuoteString:
|
||||
suffix += "\"";
|
||||
break;
|
||||
case State.Comment:
|
||||
suffix += "*/";
|
||||
break;
|
||||
}
|
||||
|
||||
suffix += ")".repeat(unclosedParenthesisCount);
|
||||
|
||||
return suffix;
|
||||
}
|
||||
|
||||
static getCompletionText(completion)
|
||||
{
|
||||
console.assert(typeof completion === "string" || completion instanceof WI.QueryResult, completion);
|
||||
|
||||
if (typeof completion === "string")
|
||||
return completion;
|
||||
|
||||
if (completion instanceof WI.QueryResult)
|
||||
return completion.value;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get values()
|
||||
{
|
||||
return this._values;
|
||||
}
|
||||
|
||||
executeQuery(query)
|
||||
{
|
||||
if (!query)
|
||||
return this._acceptEmptyPrefix ? this._values.slice() : [];
|
||||
|
||||
this._queryController ||= new WI.CSSQueryController(this._values);
|
||||
|
||||
return this._queryController.executeQuery(query);
|
||||
}
|
||||
|
||||
startsWith(prefix)
|
||||
{
|
||||
if (!prefix)
|
||||
return this._acceptEmptyPrefix ? this._values.slice() : [];
|
||||
|
||||
let firstIndex = this._firstIndexOfPrefix(prefix);
|
||||
if (firstIndex === -1)
|
||||
return [];
|
||||
|
||||
let results = [];
|
||||
while (firstIndex < this._values.length && this._values[firstIndex].startsWith(prefix))
|
||||
results.push(this._values[firstIndex++]);
|
||||
return results;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
replaceValues(values)
|
||||
{
|
||||
console.assert(Array.isArray(values), values);
|
||||
console.assert(typeof values[0] === "string", "Expect an array of string values", values);
|
||||
|
||||
this._values = values;
|
||||
this._values.sort();
|
||||
|
||||
this._queryController?.reset();
|
||||
this._queryController?.addValues(values);
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_firstIndexOfPrefix(prefix)
|
||||
{
|
||||
if (!this._values.length)
|
||||
return -1;
|
||||
if (!prefix)
|
||||
return this._acceptEmptyPrefix ? 0 : -1;
|
||||
|
||||
var maxIndex = this._values.length - 1;
|
||||
var minIndex = 0;
|
||||
var foundIndex;
|
||||
|
||||
do {
|
||||
var middleIndex = (maxIndex + minIndex) >> 1;
|
||||
if (this._values[middleIndex].startsWith(prefix)) {
|
||||
foundIndex = middleIndex;
|
||||
break;
|
||||
}
|
||||
if (this._values[middleIndex] < prefix)
|
||||
minIndex = middleIndex + 1;
|
||||
else
|
||||
maxIndex = middleIndex - 1;
|
||||
} while (minIndex <= maxIndex);
|
||||
|
||||
if (foundIndex === undefined)
|
||||
return -1;
|
||||
|
||||
while (foundIndex && this._values[foundIndex - 1].startsWith(prefix))
|
||||
foundIndex--;
|
||||
|
||||
return foundIndex;
|
||||
}
|
||||
};
|
||||
|
||||
WI.CSSCompletions.lengthUnits = new Set([
|
||||
"ch",
|
||||
"cm",
|
||||
"dvb",
|
||||
"dvh",
|
||||
"dvi",
|
||||
"dvmax",
|
||||
"dvmin",
|
||||
"dvw",
|
||||
"em",
|
||||
"ex",
|
||||
"in",
|
||||
"lvb",
|
||||
"lvh",
|
||||
"lvi",
|
||||
"lvmax",
|
||||
"lvmin",
|
||||
"lvw",
|
||||
"mm",
|
||||
"pc",
|
||||
"pt",
|
||||
"px",
|
||||
"q",
|
||||
"rem",
|
||||
"svb",
|
||||
"svh",
|
||||
"svi",
|
||||
"svmax",
|
||||
"svmin",
|
||||
"svw",
|
||||
"vb",
|
||||
"vh",
|
||||
"vi",
|
||||
"vmax",
|
||||
"vmin",
|
||||
"vw",
|
||||
]);
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CSSGrouping = class CSSGrouping extends WI.Object
|
||||
{
|
||||
constructor(nodeStyles, type, {ownerStyleSheet, id, text, sourceCodeLocation} = {})
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(nodeStyles);
|
||||
console.assert(Object.values(CSSGrouping.Type).includes(type));
|
||||
console.assert(!text || (typeof text === "string" && text.length));
|
||||
console.assert(!sourceCodeLocation || sourceCodeLocation instanceof WI.SourceCodeLocation);
|
||||
|
||||
this._nodeStyles = nodeStyles;
|
||||
this._type = type;
|
||||
|
||||
this._ownerStyleSheet = ownerStyleSheet || null;
|
||||
this._id = id || null;
|
||||
this._text = text || null;
|
||||
this._sourceCodeLocation = sourceCodeLocation || null;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get ownerStyleSheet() { return this._ownerStyleSheet; }
|
||||
get id() { return this._id; }
|
||||
get type() { return this._type; }
|
||||
get sourceCodeLocation() { return this._sourceCodeLocation; }
|
||||
|
||||
get editable()
|
||||
{
|
||||
return !!this._id && !!this.ownerStyleSheet;
|
||||
}
|
||||
|
||||
get text()
|
||||
{
|
||||
return this._text;
|
||||
}
|
||||
|
||||
async setText(newText)
|
||||
{
|
||||
console.assert(this.editable);
|
||||
if (!this.editable)
|
||||
throw "Cannot set text on non-editable CSSGrouping.";
|
||||
|
||||
newText ||= "";
|
||||
|
||||
this._nodeStyles.ignoreNextContentDidChangeForStyleSheet = this._ownerStyleSheet;
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
let {grouping: groupingPayload} = await target.CSSAgent.setGroupingHeaderText(this._id, newText);
|
||||
|
||||
target.DOMAgent.markUndoableState();
|
||||
await this._nodeStyles.refresh();
|
||||
|
||||
console.assert(groupingPayload.type == this._type);
|
||||
|
||||
console.assert(groupingPayload.ruleId);
|
||||
console.assert(groupingPayload.text);
|
||||
|
||||
this._id = groupingPayload.ruleId;
|
||||
this._text = groupingPayload.text;
|
||||
|
||||
let location = {};
|
||||
if (groupingPayload.sourceRange) {
|
||||
location.line = groupingPayload.sourceRange.startLine;
|
||||
location.column = groupingPayload.sourceRange.startColumn;
|
||||
location.documentNode = this._nodeStyles.node.ownerDocument;
|
||||
}
|
||||
|
||||
let groupingSourceCodeLocation = WI.DOMNodeStyles.createSourceCodeLocation(groupingPayload.sourceURL, location);
|
||||
this._sourceCodeLocation = WI.cssManager.styleSheetForIdentifier(this._id.styleSheetId).offsetSourceCodeLocation(groupingSourceCodeLocation);
|
||||
|
||||
this.dispatchEventToListeners(WI.CSSGrouping.Event.TextChanged);
|
||||
}
|
||||
|
||||
get isMedia()
|
||||
{
|
||||
return this._type === WI.CSSGrouping.Type.MediaRule
|
||||
|| this._type === WI.CSSGrouping.Type.MediaImportRule
|
||||
|| this._type === WI.CSSGrouping.Type.MediaLinkNode
|
||||
|| this._type === WI.CSSGrouping.Type.MediaStyleNode;
|
||||
}
|
||||
|
||||
get isSupports()
|
||||
{
|
||||
return this._type === WI.CSSGrouping.Type.SupportsRule;
|
||||
}
|
||||
|
||||
get isLayer()
|
||||
{
|
||||
return this._type === WI.CSSGrouping.Type.LayerRule
|
||||
|| this._type === WI.CSSGrouping.Type.LayerImportRule;
|
||||
}
|
||||
|
||||
get isContainer()
|
||||
{
|
||||
return this._type === WI.CSSGrouping.Type.ContainerRule;
|
||||
}
|
||||
|
||||
get isStyle()
|
||||
{
|
||||
return this._type === WI.CSSGrouping.Type.StyleRule;
|
||||
}
|
||||
|
||||
get prefix()
|
||||
{
|
||||
if (this.isSupports)
|
||||
return "@supports";
|
||||
|
||||
if (this.isLayer)
|
||||
return "@layer";
|
||||
|
||||
if (this.isContainer)
|
||||
return "@container";
|
||||
|
||||
if (this.isStyle)
|
||||
return null;
|
||||
|
||||
console.assert(this.isMedia);
|
||||
return "@media";
|
||||
}
|
||||
};
|
||||
|
||||
WI.CSSGrouping.Type = {
|
||||
MediaRule: "media-rule",
|
||||
MediaImportRule: "media-import-rule",
|
||||
MediaLinkNode: "media-link-node",
|
||||
MediaStyleNode: "media-style-node",
|
||||
SupportsRule: "supports-rule",
|
||||
LayerRule: "layer-rule",
|
||||
LayerImportRule: "layer-import-rule",
|
||||
ContainerRule: "container-rule",
|
||||
StyleRule: "style-rule",
|
||||
};
|
||||
|
||||
WI.CSSGrouping.Event = {
|
||||
TextChanged: "css-grouping-event-grouping-text-changed",
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CSSMedia = class CSSMedia
|
||||
{
|
||||
constructor(type, text, sourceCodeLocation)
|
||||
{
|
||||
console.assert(!sourceCodeLocation || sourceCodeLocation instanceof WI.SourceCodeLocation);
|
||||
|
||||
this._type = type || null;
|
||||
this._text = text || "";
|
||||
this._sourceCodeLocation = sourceCodeLocation || null;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get type() { return this._type; }
|
||||
get text() { return this._text; }
|
||||
get sourceCodeLocation() { return this._sourceCodeLocation; }
|
||||
};
|
||||
|
||||
WI.CSSMedia.Type = {
|
||||
MediaRule: "css-media-type-media-rule",
|
||||
ImportRule: "css-media-type-import-rule",
|
||||
LinkedStyleSheet: "css-media-type-linked-stylesheet",
|
||||
InlineStyleSheet: "css-media-type-inline-stylesheet"
|
||||
};
|
||||
@@ -0,0 +1,672 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CSSProperty = class CSSProperty extends WI.Object
|
||||
{
|
||||
constructor(index, text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange)
|
||||
{
|
||||
WI.CSSProperty._initializePropertyNameCounts();
|
||||
|
||||
super();
|
||||
|
||||
this._ownerStyle = null;
|
||||
this._index = index;
|
||||
this._overridingProperty = null;
|
||||
this._initialState = null;
|
||||
this._modified = false;
|
||||
this._isUpdatingText = false;
|
||||
this._isNewProperty = false;
|
||||
|
||||
this.update(text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange, true);
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static isVariable(name)
|
||||
{
|
||||
return name.startsWith("--") && name.length > 2;
|
||||
}
|
||||
|
||||
static isInheritedPropertyName(name)
|
||||
{
|
||||
console.assert(typeof name === "string");
|
||||
if (WI.CSSKeywordCompletions.InheritedProperties.has(name))
|
||||
return true;
|
||||
return WI.CSSProperty.isVariable(name);
|
||||
}
|
||||
|
||||
// FIXME: <https://webkit.org/b/226647> This naively collects variable-like names used in values. It should be hardened.
|
||||
static findVariableNames(string)
|
||||
{
|
||||
const prefix = "var(--";
|
||||
let prefixCursor = 0;
|
||||
let cursor = 0;
|
||||
let nameStartIndex = 0;
|
||||
let names = [];
|
||||
|
||||
function isTerminatingChar(char) {
|
||||
return char === ")" || char === "," || char === " " || char === "\n" || char === "\t";
|
||||
}
|
||||
|
||||
while (cursor < string.length) {
|
||||
if (nameStartIndex && isTerminatingChar(string.charAt(cursor))) {
|
||||
names.push("--" + string.substring(nameStartIndex, cursor));
|
||||
nameStartIndex = 0;
|
||||
}
|
||||
|
||||
if (prefixCursor === prefix.length) {
|
||||
prefixCursor = 0;
|
||||
nameStartIndex = cursor;
|
||||
}
|
||||
|
||||
if (string.charAt(cursor) === prefix.charAt(prefixCursor))
|
||||
prefixCursor++;
|
||||
|
||||
cursor++;
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
static sortPreferringNonPrefixed(a, b)
|
||||
{
|
||||
let aIsPrefixed = a[0] === "-" && a[1] !== "-";
|
||||
let bIsPrefixed = b[0] === "-" && b[1] !== "-";
|
||||
if (!aIsPrefixed && bIsPrefixed)
|
||||
return -1;
|
||||
if (aIsPrefixed && !bIsPrefixed)
|
||||
return 1;
|
||||
|
||||
return a.extendedLocaleCompare(b);
|
||||
}
|
||||
|
||||
static sortByPropertyNameUsageCount(propertyNameA, propertyNameB)
|
||||
{
|
||||
let countA = WI.CSSProperty._cachedNameCounts[propertyNameA];
|
||||
let countB = WI.CSSProperty._cachedNameCounts[propertyNameB];
|
||||
|
||||
const minimumCount = 100;
|
||||
let validA = countA >= minimumCount;
|
||||
let validB = countB >= minimumCount;
|
||||
|
||||
if (validA && !validB)
|
||||
return -1;
|
||||
if (!validA && validB)
|
||||
return 1;
|
||||
|
||||
if (validA && validB) {
|
||||
if (countA !== countB)
|
||||
return countB - countA;
|
||||
|
||||
let canonicalPropertyNameA = WI.cssManager.canonicalNameForPropertyName(propertyNameA);
|
||||
let canonicalPropertyNameB = WI.cssManager.canonicalNameForPropertyName(propertyNameB);
|
||||
if (canonicalPropertyNameA !== propertyNameA || canonicalPropertyNameB !== propertyNameB)
|
||||
return WI.CSSProperty.sortByPropertyNameUsageCount(canonicalPropertyNameA, canonicalPropertyNameB);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static indexOfCompletionForMostUsedPropertyName(completions)
|
||||
{
|
||||
let highestRankCompletions = completions;
|
||||
if (highestRankCompletions.every((completion) => completion instanceof WI.QueryResult)) {
|
||||
let highestRankValue = -1;
|
||||
for (let completion of completions) {
|
||||
if (completion.rank > highestRankValue) {
|
||||
highestRankValue = completion.rank;
|
||||
highestRankCompletions = [];
|
||||
}
|
||||
|
||||
if (completion.rank === highestRankValue)
|
||||
highestRankCompletions.push(completion);
|
||||
}
|
||||
}
|
||||
let mostUsedHighestRankCompletion = highestRankCompletions.min((a, b) => WI.CSSProperty.sortByPropertyNameUsageCount(WI.CSSCompletions.getCompletionText(a), WI.CSSCompletions.getCompletionText(b)));
|
||||
return completions.indexOf(mostUsedHighestRankCompletion);
|
||||
}
|
||||
|
||||
static _initializePropertyNameCounts()
|
||||
{
|
||||
if (WI.CSSProperty._cachedNameCounts)
|
||||
return;
|
||||
|
||||
WI.CSSProperty._cachedNameCounts = {};
|
||||
|
||||
WI.CSSProperty._storedNameCountsQueue = new Promise((resolve, reject) => {
|
||||
WI.objectStores.cssPropertyNameCounts.getAllKeys().then((propertyNames) => {
|
||||
Promise.allSettled(propertyNames.map(async (propertyName) => {
|
||||
let storedCount = await WI.objectStores.cssPropertyNameCounts.get(propertyName);
|
||||
|
||||
WI.CSSProperty._cachedNameCounts[propertyName] = (WI.CSSProperty._cachedNameCounts[propertyName] || 0) + storedCount;
|
||||
}))
|
||||
.then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get ownerStyle()
|
||||
{
|
||||
return this._ownerStyle;
|
||||
}
|
||||
|
||||
set ownerStyle(ownerStyle)
|
||||
{
|
||||
this._ownerStyle = ownerStyle || null;
|
||||
}
|
||||
|
||||
get index()
|
||||
{
|
||||
return this._index;
|
||||
}
|
||||
|
||||
set index(index)
|
||||
{
|
||||
this._index = index;
|
||||
}
|
||||
|
||||
update(text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange, dontFireEvents)
|
||||
{
|
||||
// Locked CSSProperty can still be updated from the back-end when the text matches.
|
||||
// We need to do this to keep attributes such as valid and overridden up to date.
|
||||
if (this._ownerStyle && this._ownerStyle.locked && text !== this._text)
|
||||
return;
|
||||
|
||||
text = text || "";
|
||||
name = name || "";
|
||||
value = value || "";
|
||||
priority = priority || "";
|
||||
enabled = enabled || false;
|
||||
overridden = overridden || false;
|
||||
implicit = implicit || false;
|
||||
anonymous = anonymous || false;
|
||||
valid = valid || false;
|
||||
|
||||
var changed = false;
|
||||
|
||||
if (!dontFireEvents) {
|
||||
changed = this._name !== name || this._rawValue !== value || this._priority !== priority ||
|
||||
this._enabled !== enabled || this._implicit !== implicit || this._anonymous !== anonymous || this._valid !== valid;
|
||||
}
|
||||
|
||||
// Use the setter for overridden if we want to fire events since the
|
||||
// OverriddenStatusChanged event coalesces changes before it fires.
|
||||
if (!dontFireEvents)
|
||||
this.overridden = overridden;
|
||||
else
|
||||
this._overridden = overridden;
|
||||
|
||||
if (!overridden)
|
||||
this._overridingProperty = null;
|
||||
|
||||
this._text = text;
|
||||
this._rawValue = value;
|
||||
this._value = undefined;
|
||||
this._priority = priority;
|
||||
this._enabled = enabled;
|
||||
this._implicit = implicit;
|
||||
this._anonymous = anonymous;
|
||||
this._inherited = WI.CSSProperty.isInheritedPropertyName(name);
|
||||
this._valid = valid;
|
||||
this._isVariable = WI.CSSProperty.isVariable(name);
|
||||
this._styleSheetTextRange = styleSheetTextRange || null;
|
||||
|
||||
this._rawValueNewlineIndent = "";
|
||||
if (this._rawValue) {
|
||||
let match = this._rawValue.match(/^[^\n]+\n(\s*)/);
|
||||
if (match)
|
||||
this._rawValueNewlineIndent = match[1];
|
||||
}
|
||||
this._rawValue = this._rawValue.replace(/\n\s+/g, "\n");
|
||||
|
||||
this._isShorthand = undefined;
|
||||
this._shorthandPropertyNames = undefined;
|
||||
|
||||
this._updateName(name);
|
||||
this._relatedShorthandProperty = null;
|
||||
this._relatedLonghandProperties = [];
|
||||
|
||||
// Clear computed properties.
|
||||
delete this._styleDeclarationTextRange;
|
||||
delete this._canonicalName;
|
||||
delete this._hasOtherVendorNameOrKeyword;
|
||||
|
||||
if (changed)
|
||||
this.dispatchEventToListeners(WI.CSSProperty.Event.Changed);
|
||||
}
|
||||
|
||||
remove()
|
||||
{
|
||||
this._markModified();
|
||||
|
||||
// Setting name or value to an empty string removes the entire CSSProperty.
|
||||
this._updateName("");
|
||||
const forceRemove = true;
|
||||
this._updateStyleText(forceRemove);
|
||||
}
|
||||
|
||||
commentOut(disabled)
|
||||
{
|
||||
console.assert(this.editable);
|
||||
if (this._enabled === !disabled)
|
||||
return;
|
||||
|
||||
this._markModified();
|
||||
this._enabled = !disabled;
|
||||
|
||||
if (disabled)
|
||||
this.text = "/* " + this._text + " */";
|
||||
else
|
||||
this.text = this._text.slice(2, -2).trim();
|
||||
}
|
||||
|
||||
get text()
|
||||
{
|
||||
return this._text;
|
||||
}
|
||||
|
||||
set text(newText)
|
||||
{
|
||||
newText = newText.trim();
|
||||
if (this._text === newText)
|
||||
return;
|
||||
|
||||
this._markModified();
|
||||
this._text = newText;
|
||||
this._isUpdatingText = true;
|
||||
this._updateOwnerStyleText();
|
||||
this._isUpdatingText = false;
|
||||
}
|
||||
|
||||
get formattedText()
|
||||
{
|
||||
if (this._isUpdatingText)
|
||||
return this._text;
|
||||
|
||||
if (!this._name)
|
||||
return "";
|
||||
|
||||
let text = `${this._name}: ${this._rawValue};`;
|
||||
if (!this._enabled)
|
||||
text = "/* " + text + " */";
|
||||
return text;
|
||||
}
|
||||
|
||||
get modified()
|
||||
{
|
||||
return this._modified;
|
||||
}
|
||||
|
||||
set modified(value)
|
||||
{
|
||||
if (this._modified === value)
|
||||
return;
|
||||
|
||||
this._modified = value;
|
||||
this.dispatchEventToListeners(WI.CSSProperty.Event.ModifiedChanged);
|
||||
}
|
||||
|
||||
get name()
|
||||
{
|
||||
return this._name;
|
||||
}
|
||||
|
||||
set name(name)
|
||||
{
|
||||
if (name === this._name)
|
||||
return;
|
||||
|
||||
if (!name) {
|
||||
// Deleting property name causes CSSProperty to be detached from CSSStyleDeclaration.
|
||||
console.assert(!isNaN(this._index), this);
|
||||
this._indexBeforeDetached = this._index;
|
||||
} else if (!isNaN(this._indexBeforeDetached) && isNaN(this._index)) {
|
||||
// Reattach CSSProperty.
|
||||
console.assert(!this._ownerStyle.properties.includes(this), this);
|
||||
this._ownerStyle.insertProperty(this, this._indexBeforeDetached);
|
||||
this._indexBeforeDetached = NaN;
|
||||
}
|
||||
|
||||
this._markModified();
|
||||
this._updateName(name);
|
||||
this._updateStyleText();
|
||||
}
|
||||
|
||||
get canonicalName()
|
||||
{
|
||||
if (this._canonicalName)
|
||||
return this._canonicalName;
|
||||
|
||||
this._canonicalName = WI.cssManager.canonicalNameForPropertyName(this.name);
|
||||
|
||||
return this._canonicalName;
|
||||
}
|
||||
|
||||
// FIXME: Remove current value getter and rename rawValue to value once the old styles sidebar is removed.
|
||||
get value()
|
||||
{
|
||||
if (!this._value)
|
||||
this._value = this._rawValue.replace(/\s*!important\s*$/, "");
|
||||
|
||||
return this._value;
|
||||
}
|
||||
|
||||
get rawValue()
|
||||
{
|
||||
return this._rawValue;
|
||||
}
|
||||
|
||||
set rawValue(value)
|
||||
{
|
||||
if (value === this._rawValue)
|
||||
return;
|
||||
|
||||
this._markModified();
|
||||
|
||||
let suffix = WI.CSSCompletions.completeUnbalancedValue(value);
|
||||
if (suffix)
|
||||
value += suffix;
|
||||
|
||||
this._rawValue = value;
|
||||
this._value = undefined;
|
||||
this._updateStyleText();
|
||||
}
|
||||
|
||||
get important()
|
||||
{
|
||||
return this.priority === "important";
|
||||
}
|
||||
|
||||
get priority() { return this._priority; }
|
||||
|
||||
get attached()
|
||||
{
|
||||
return this._enabled && this._ownerStyle && (!isNaN(this._index) || this._ownerStyle.type === WI.CSSStyleDeclaration.Type.Computed);
|
||||
}
|
||||
|
||||
// Only commented out properties are disabled.
|
||||
get enabled() { return this._enabled; }
|
||||
|
||||
get overridden() { return this._overridden; }
|
||||
set overridden(overridden)
|
||||
{
|
||||
overridden = overridden || false;
|
||||
|
||||
if (this._overridden === overridden)
|
||||
return;
|
||||
|
||||
if (!overridden)
|
||||
this._overridingProperty = null;
|
||||
|
||||
var previousOverridden = this._overridden;
|
||||
|
||||
this._overridden = overridden;
|
||||
|
||||
if (this._overriddenStatusChangedTimeout)
|
||||
return;
|
||||
|
||||
function delayed()
|
||||
{
|
||||
delete this._overriddenStatusChangedTimeout;
|
||||
|
||||
if (this._overridden === previousOverridden)
|
||||
return;
|
||||
|
||||
this.dispatchEventToListeners(WI.CSSProperty.Event.OverriddenStatusChanged);
|
||||
}
|
||||
|
||||
this._overriddenStatusChangedTimeout = setTimeout(delayed.bind(this), 0);
|
||||
}
|
||||
|
||||
get overridingProperty()
|
||||
{
|
||||
console.assert(this._overridden);
|
||||
return this._overridingProperty;
|
||||
}
|
||||
|
||||
set overridingProperty(effectiveProperty)
|
||||
{
|
||||
if (!WI.settings.experimentalEnableStylesJumpToEffective.value)
|
||||
return;
|
||||
|
||||
console.assert(this !== effectiveProperty, `Property "${this.formattedText}" can't override itself.`, this);
|
||||
this._overridingProperty = effectiveProperty || null;
|
||||
}
|
||||
|
||||
get implicit() { return this._implicit; }
|
||||
set implicit(implicit) { this._implicit = implicit; }
|
||||
|
||||
get anonymous() { return this._anonymous; }
|
||||
get inherited() { return this._inherited; }
|
||||
get valid() { return this._valid; }
|
||||
get isVariable() { return this._isVariable; }
|
||||
get styleSheetTextRange() { return this._styleSheetTextRange; }
|
||||
|
||||
get initialState()
|
||||
{
|
||||
return this._initialState;
|
||||
}
|
||||
|
||||
get editable()
|
||||
{
|
||||
return !!(this._styleSheetTextRange && this._ownerStyle && this._ownerStyle.styleSheetTextRange);
|
||||
}
|
||||
|
||||
get styleDeclarationTextRange()
|
||||
{
|
||||
if ("_styleDeclarationTextRange" in this)
|
||||
return this._styleDeclarationTextRange;
|
||||
|
||||
if (!this._ownerStyle || !this._styleSheetTextRange)
|
||||
return null;
|
||||
|
||||
var styleTextRange = this._ownerStyle.styleSheetTextRange;
|
||||
if (!styleTextRange)
|
||||
return null;
|
||||
|
||||
var startLine = this._styleSheetTextRange.startLine - styleTextRange.startLine;
|
||||
var endLine = this._styleSheetTextRange.endLine - styleTextRange.startLine;
|
||||
|
||||
var startColumn = this._styleSheetTextRange.startColumn;
|
||||
if (!startLine)
|
||||
startColumn -= styleTextRange.startColumn;
|
||||
|
||||
var endColumn = this._styleSheetTextRange.endColumn;
|
||||
if (!endLine)
|
||||
endColumn -= styleTextRange.startColumn;
|
||||
|
||||
this._styleDeclarationTextRange = new WI.TextRange(startLine, startColumn, endLine, endColumn);
|
||||
|
||||
return this._styleDeclarationTextRange;
|
||||
}
|
||||
|
||||
get relatedShorthandProperty() { return this._relatedShorthandProperty; }
|
||||
set relatedShorthandProperty(property)
|
||||
{
|
||||
this._relatedShorthandProperty = property || null;
|
||||
}
|
||||
|
||||
get relatedLonghandProperties() { return this._relatedLonghandProperties; }
|
||||
|
||||
addRelatedLonghandProperty(property)
|
||||
{
|
||||
this._relatedLonghandProperties.push(property);
|
||||
}
|
||||
|
||||
clearRelatedLonghandProperties(property)
|
||||
{
|
||||
this._relatedLonghandProperties = [];
|
||||
}
|
||||
|
||||
get isShorthand()
|
||||
{
|
||||
if (this._isShorthand === undefined) {
|
||||
this._isShorthand = WI.CSSKeywordCompletions.LonghandNamesForShorthandProperty.has(this._name);
|
||||
if (this._isShorthand) {
|
||||
let longhands = WI.CSSKeywordCompletions.LonghandNamesForShorthandProperty.get(this._name);
|
||||
if (longhands && longhands.length === 1)
|
||||
this._isShorthand = false;
|
||||
}
|
||||
}
|
||||
return this._isShorthand;
|
||||
}
|
||||
|
||||
get shorthandPropertyNames()
|
||||
{
|
||||
if (!this._shorthandPropertyNames) {
|
||||
this._shorthandPropertyNames = WI.CSSKeywordCompletions.ShorthandNamesForLongHandProperty.get(this._name) || [];
|
||||
this._shorthandPropertyNames.remove("all");
|
||||
}
|
||||
return this._shorthandPropertyNames;
|
||||
}
|
||||
|
||||
get isNewProperty() { return this._isNewProperty; }
|
||||
set isNewProperty(value) { this._isNewProperty = value; }
|
||||
|
||||
hasOtherVendorNameOrKeyword()
|
||||
{
|
||||
if ("_hasOtherVendorNameOrKeyword" in this)
|
||||
return this._hasOtherVendorNameOrKeyword;
|
||||
|
||||
this._hasOtherVendorNameOrKeyword = WI.cssManager.propertyNameHasOtherVendorPrefix(this.name) || WI.cssManager.propertyValueHasOtherVendorKeyword(this.value);
|
||||
|
||||
return this._hasOtherVendorNameOrKeyword;
|
||||
}
|
||||
|
||||
equals(property)
|
||||
{
|
||||
if (property === this)
|
||||
return true;
|
||||
|
||||
if (!property)
|
||||
return false;
|
||||
|
||||
return this._name === property.name && this._rawValue === property.rawValue && this._enabled === property.enabled;
|
||||
}
|
||||
|
||||
clone()
|
||||
{
|
||||
let cssProperty = new WI.CSSProperty(
|
||||
this._index,
|
||||
this._text,
|
||||
this._name,
|
||||
this._rawValue,
|
||||
this._priority,
|
||||
this._enabled,
|
||||
this._overridden,
|
||||
this._implicit,
|
||||
this._anonymous,
|
||||
this._valid,
|
||||
this._styleSheetTextRange);
|
||||
|
||||
cssProperty.ownerStyle = this._ownerStyle;
|
||||
|
||||
return cssProperty;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_updateName(name)
|
||||
{
|
||||
if (name === this._name)
|
||||
return;
|
||||
|
||||
let changeCount = (propertyName, delta) => {
|
||||
if (this._implicit || this._anonymous || !this._enabled)
|
||||
return;
|
||||
|
||||
if (!propertyName || WI.CSSProperty.isVariable(propertyName))
|
||||
return;
|
||||
|
||||
let cachedCount = WI.CSSProperty._cachedNameCounts[propertyName];
|
||||
|
||||
// Allow property counts to be updated if the property name has already been counted before.
|
||||
// This can happen when inspecting a device that has different CSS properties enabled.
|
||||
if (isNaN(cachedCount) && !WI.cssManager.propertyNameCompletions.isValidPropertyName(propertyName))
|
||||
return;
|
||||
|
||||
console.assert(delta > 0 || cachedCount >= delta, cachedCount, delta);
|
||||
WI.CSSProperty._cachedNameCounts[propertyName] = Math.max(0, (cachedCount || 0) + delta);
|
||||
|
||||
WI.CSSProperty._storedNameCountsQueue = WI.CSSProperty._storedNameCountsQueue.finally(async () => {
|
||||
let storedCount = await WI.objectStores.cssPropertyNameCounts.get(propertyName);
|
||||
|
||||
console.assert(delta > 0 || storedCount >= delta, storedCount, delta);
|
||||
await WI.objectStores.cssPropertyNameCounts.put(Math.max(0, (storedCount || 0) + delta), propertyName);
|
||||
});
|
||||
|
||||
if (propertyName !== this.canonicalName)
|
||||
changeCount(this.canonicalName, delta);
|
||||
};
|
||||
|
||||
changeCount(this._name, -1);
|
||||
this._name = name;
|
||||
changeCount(this._name, 1);
|
||||
}
|
||||
|
||||
_updateStyleText(forceRemove = false)
|
||||
{
|
||||
let text = "";
|
||||
|
||||
if (this._name && this._rawValue) {
|
||||
let value = this._rawValue.replace(/\n/g, "\n" + this._rawValueNewlineIndent);
|
||||
text = this._name + ": " + value + ";";
|
||||
}
|
||||
|
||||
this._text = text;
|
||||
|
||||
if (forceRemove)
|
||||
this._ownerStyle.removeProperty(this);
|
||||
|
||||
this._updateOwnerStyleText();
|
||||
}
|
||||
|
||||
_updateOwnerStyleText()
|
||||
{
|
||||
console.assert(this._ownerStyle);
|
||||
if (!this._ownerStyle)
|
||||
return;
|
||||
|
||||
this._ownerStyle.text = this._ownerStyle.generateFormattedText({multiline: this._ownerStyle.type === WI.CSSStyleDeclaration.Type.Rule});
|
||||
this._ownerStyle.updatePropertiesModifiedState();
|
||||
}
|
||||
|
||||
_markModified()
|
||||
{
|
||||
if (this._ownerStyle)
|
||||
this._ownerStyle.markModified();
|
||||
}
|
||||
};
|
||||
|
||||
WI.CSSProperty._cachedNameCounts = null;
|
||||
WI.CSSProperty._storedNameCountsQueue = null;
|
||||
|
||||
WI.CSSProperty.Event = {
|
||||
Changed: "css-property-changed",
|
||||
ModifiedChanged: "css-property-modified-changed",
|
||||
OverriddenStatusChanged: "css-property-overridden-status-changed"
|
||||
};
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CSSPropertyNameCompletions = class CSSPropertyNameCompletions extends WI.CSSCompletions
|
||||
{
|
||||
constructor(properties, options = {})
|
||||
{
|
||||
console.assert(Array.isArray(properties), properties);
|
||||
console.assert(properties[0].name, "Expected an array of objects with `name` key", properties);
|
||||
|
||||
let values = [];
|
||||
for (let property of properties) {
|
||||
console.assert(property.name);
|
||||
|
||||
values.push(property.name);
|
||||
if (Array.isArray(property.aliases))
|
||||
values.pushAll(property.aliases);
|
||||
}
|
||||
|
||||
super(values, options);
|
||||
|
||||
this._cachedSortedPropertyNames = this.values.slice();
|
||||
this._needsVariablesFromInspectedNode = true;
|
||||
this._inspectedDOMNodeStyles = null;
|
||||
|
||||
WI.domManager.addEventListener(WI.DOMManager.Event.InspectedNodeChanged, this._handleInspectedNodeChanged, this);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
isValidPropertyName(name)
|
||||
{
|
||||
return this.values.includes(name);
|
||||
}
|
||||
|
||||
executeQuery(query)
|
||||
{
|
||||
this._updateValuesWithLatestCSSVariablesIfNeeded();
|
||||
|
||||
return super.executeQuery(query);
|
||||
}
|
||||
|
||||
startsWith(prefix)
|
||||
{
|
||||
this._updateValuesWithLatestCSSVariablesIfNeeded();
|
||||
|
||||
return super.startsWith(prefix);
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_updateValuesWithLatestCSSVariablesIfNeeded()
|
||||
{
|
||||
if (!this._needsVariablesFromInspectedNode)
|
||||
return;
|
||||
|
||||
// An inspected node is not guaranteed to exist when unit testing.
|
||||
if (!WI.domManager.inspectedNode) {
|
||||
this._needsVariablesFromInspectedNode = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let values = Array.from(WI.cssManager.stylesForNode(WI.domManager.inspectedNode).allCSSVariables);
|
||||
values.pushAll(this._cachedSortedPropertyNames);
|
||||
this.replaceValues(values);
|
||||
|
||||
this._needsVariablesFromInspectedNode = false;
|
||||
}
|
||||
|
||||
_handleInspectedNodeChanged(event)
|
||||
{
|
||||
this._needsVariablesFromInspectedNode = true;
|
||||
|
||||
this._inspectedDOMNodeStyles?.removeEventListener(WI.DOMNodeStyles.Event.NeedsRefresh, this._handleNodesStylesNeedsRefresh, this);
|
||||
this._inspectedDOMNodeStyles = WI.cssManager.stylesForNode(WI.domManager.inspectedNode);
|
||||
this._inspectedDOMNodeStyles.addEventListener(WI.DOMNodeStyles.Event.NeedsRefresh, this._handleNodesStylesNeedsRefresh, this);
|
||||
}
|
||||
|
||||
_handleNodesStylesNeedsRefresh(event)
|
||||
{
|
||||
this._needsVariablesFromInspectedNode = true;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CSSRule = class CSSRule extends WI.Object
|
||||
{
|
||||
constructor(nodeStyles, ownerStyleSheet, id, type, sourceCodeLocation, selectorText, selectors, matchedSelectorIndices, style, groupings)
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(nodeStyles);
|
||||
this._nodeStyles = nodeStyles;
|
||||
|
||||
this._ownerStyleSheet = ownerStyleSheet || null;
|
||||
this._id = id || null;
|
||||
this._type = type || null;
|
||||
this._initialState = null;
|
||||
|
||||
this.update(sourceCodeLocation, selectorText, selectors, matchedSelectorIndices, style, groupings, true);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get ownerStyleSheet() { return this._ownerStyleSheet; }
|
||||
get id() { return this._id; }
|
||||
get type() { return this._type; }
|
||||
get initialState() { return this._initialState; }
|
||||
get sourceCodeLocation() { return this._sourceCodeLocation; }
|
||||
get selectors() { return this._selectors; }
|
||||
get matchedSelectorIndices() { return this._matchedSelectorIndices; }
|
||||
get style() { return this._style; }
|
||||
get groupings() { return this._groupings; }
|
||||
|
||||
get editable()
|
||||
{
|
||||
return !!this._id && this._type !== WI.CSSStyleSheet.Type.UserAgent;
|
||||
}
|
||||
|
||||
get selectorText()
|
||||
{
|
||||
return this._selectorText;
|
||||
}
|
||||
|
||||
setSelectorText(selectorText)
|
||||
{
|
||||
console.assert(this.editable);
|
||||
if (!this.editable)
|
||||
return Promise.reject();
|
||||
|
||||
return this._nodeStyles.changeRuleSelector(this, selectorText).then(this._selectorResolved.bind(this));
|
||||
}
|
||||
|
||||
update(sourceCodeLocation, selectorText, selectors, matchedSelectorIndices, style, groupings)
|
||||
{
|
||||
sourceCodeLocation = sourceCodeLocation || null;
|
||||
selectorText = selectorText || "";
|
||||
selectors = selectors || [];
|
||||
matchedSelectorIndices = matchedSelectorIndices || [];
|
||||
style = style || null;
|
||||
groupings = groupings || [];
|
||||
|
||||
if (this._style)
|
||||
this._style.ownerRule = null;
|
||||
|
||||
this._sourceCodeLocation = sourceCodeLocation;
|
||||
this._selectorText = selectorText;
|
||||
this._selectors = selectors;
|
||||
this._matchedSelectorIndices = matchedSelectorIndices;
|
||||
this._style = style;
|
||||
this._groupings = groupings;
|
||||
|
||||
if (this._style)
|
||||
this._style.ownerRule = this;
|
||||
}
|
||||
|
||||
isEqualTo(rule)
|
||||
{
|
||||
if (!rule)
|
||||
return false;
|
||||
|
||||
return Object.shallowEqual(this._id, rule.id);
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
get nodeStyles()
|
||||
{
|
||||
return this._nodeStyles;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
// This method only needs to be called for CSS rules that don't match the selected DOM node.
|
||||
// CSS rules that match the selected DOM node get updated by WI.DOMNodeStyles.prototype._parseRulePayload.
|
||||
_selectorResolved(rulePayload)
|
||||
{
|
||||
if (!rulePayload)
|
||||
return;
|
||||
|
||||
let selectorText = rulePayload.selectorList.text;
|
||||
if (selectorText === this._selectorText)
|
||||
return;
|
||||
|
||||
let selectors = WI.DOMNodeStyles.parseSelectorListPayload(rulePayload.selectorList);
|
||||
|
||||
let sourceCodeLocation = null;
|
||||
let sourceRange = rulePayload.selectorList.range;
|
||||
if (sourceRange) {
|
||||
sourceCodeLocation = WI.DOMNodeStyles.createSourceCodeLocation(rulePayload.sourceURL, {
|
||||
line: sourceRange.startLine,
|
||||
column: sourceRange.startColumn,
|
||||
documentNode: this._nodeStyles.node.ownerDocument,
|
||||
});
|
||||
}
|
||||
|
||||
if (this._ownerStyleSheet) {
|
||||
if (!sourceCodeLocation && sourceRange)
|
||||
sourceCodeLocation = this._ownerStyleSheet.createSourceCodeLocation(sourceRange.startLine, sourceRange.startColumn);
|
||||
sourceCodeLocation = this._ownerStyleSheet.offsetSourceCodeLocation(sourceCodeLocation);
|
||||
}
|
||||
|
||||
this.update(sourceCodeLocation, selectorText, selectors, [], this._style, this._groupings);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CSSSelector = class CSSSelector
|
||||
{
|
||||
constructor(text, specificity, dynamic)
|
||||
{
|
||||
console.assert(text);
|
||||
|
||||
this._text = text;
|
||||
this._specificity = specificity || null;
|
||||
this._dynamic = dynamic || false;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get text() { return this._text; }
|
||||
get specificity() { return this._specificity; }
|
||||
get dynamic() { return this._dynamic; }
|
||||
|
||||
isPseudoSelector()
|
||||
{
|
||||
return Object.values(WI.CSSManager.PseudoSelectorNames).some((pseudoId) => (new RegExp("(?:\\b|^):{1,2}(?:-webkit-)?" + pseudoId + "(?:\\b|$)")).test(this._text));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,629 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CSSStyleDeclaration = class CSSStyleDeclaration extends WI.Object
|
||||
{
|
||||
constructor(nodeStyles, ownerStyleSheet, id, type, node, inherited, text, properties, styleSheetTextRange)
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(nodeStyles);
|
||||
this._nodeStyles = nodeStyles;
|
||||
|
||||
this._ownerRule = null;
|
||||
|
||||
this._ownerStyleSheet = ownerStyleSheet || null;
|
||||
this._id = id || null;
|
||||
this._type = type || null;
|
||||
this._node = node || null;
|
||||
this._inherited = inherited || false;
|
||||
|
||||
this._initialState = null;
|
||||
this._updatesInProgressCount = 0;
|
||||
this._pendingPropertiesChanged = false;
|
||||
this._locked = false;
|
||||
this._pendingProperties = [];
|
||||
this._propertyNameMap = {};
|
||||
|
||||
this._properties = [];
|
||||
this._enabledProperties = null;
|
||||
this._visibleProperties = null;
|
||||
this._variablesForType = new Map;
|
||||
|
||||
this.update(text, properties, styleSheetTextRange, {dontFireEvents: true});
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get initialState() { return this._initialState; }
|
||||
|
||||
get id()
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get stringId()
|
||||
{
|
||||
if (this._id)
|
||||
return this._id.styleSheetId + "/" + this._id.ordinal;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
get ownerStyleSheet()
|
||||
{
|
||||
return this._ownerStyleSheet;
|
||||
}
|
||||
|
||||
get type()
|
||||
{
|
||||
return this._type;
|
||||
}
|
||||
|
||||
get inherited()
|
||||
{
|
||||
return this._inherited;
|
||||
}
|
||||
|
||||
get node()
|
||||
{
|
||||
return this._node;
|
||||
}
|
||||
|
||||
get editable()
|
||||
{
|
||||
if (!this._id)
|
||||
return false;
|
||||
|
||||
if (this._type === WI.CSSStyleDeclaration.Type.Rule)
|
||||
return this._ownerRule && this._ownerRule.editable;
|
||||
|
||||
if (this._type === WI.CSSStyleDeclaration.Type.Inline)
|
||||
return !this._node.isInUserAgentShadowTree() || WI.DOMManager.supportsEditingUserAgentShadowTrees();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
get selectorEditable()
|
||||
{
|
||||
return this._ownerRule && this._ownerRule.editable && InspectorBackend.hasCommand("CSS.setRuleSelector");
|
||||
}
|
||||
|
||||
get locked() { return this._locked; }
|
||||
set locked(value) { this._locked = value; }
|
||||
|
||||
variablesForType(type)
|
||||
{
|
||||
console.assert(Object.values(WI.CSSStyleDeclaration.VariablesGroupType).includes(type), type);
|
||||
|
||||
let variables = this._variablesForType.get(type);
|
||||
if (variables)
|
||||
return variables;
|
||||
|
||||
// Will iterate in order through type checkers for each CSS variable to identify its type.
|
||||
// The catch-all "other" must always be last.
|
||||
const typeCheckFunctions = [
|
||||
{
|
||||
type: WI.CSSStyleDeclaration.VariablesGroupType.Colors,
|
||||
checker: (property) => WI.Color.fromString(property.value),
|
||||
},
|
||||
{
|
||||
type: WI.CSSStyleDeclaration.VariablesGroupType.Dimensions,
|
||||
checker: (property) => /^-?\d+(\.\d+)?\D+$/.test(property.value),
|
||||
},
|
||||
{
|
||||
type: WI.CSSStyleDeclaration.VariablesGroupType.Numbers,
|
||||
checker: (property) => /^-?\d+(\.\d+)?$/.test(property.value),
|
||||
},
|
||||
{
|
||||
type: WI.CSSStyleDeclaration.VariablesGroupType.Other,
|
||||
checker: (property) => true,
|
||||
},
|
||||
];
|
||||
|
||||
// Ensure all types have a list. Empty lists are a signal to views to skip rendering.
|
||||
for (let {type} of typeCheckFunctions)
|
||||
this._variablesForType.set(type, []);
|
||||
|
||||
for (let property of this._properties) {
|
||||
if (!property.isVariable)
|
||||
continue;
|
||||
|
||||
for (let {type, checker} of typeCheckFunctions) {
|
||||
if (checker(property)) {
|
||||
this._variablesForType.get(type).push(property);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this._variablesForType.get(type);
|
||||
}
|
||||
|
||||
update(text, properties, styleSheetTextRange, options = {})
|
||||
{
|
||||
let dontFireEvents = options.dontFireEvents || false;
|
||||
|
||||
// When two consequent setText calls happen (A and B), only update when the last call (B) is finished.
|
||||
// Front-end: A B
|
||||
// Back-end: A B
|
||||
// _updatesInProgressCount: 0 1 2 1 0
|
||||
// ^
|
||||
// update only happens here
|
||||
if (this._updatesInProgressCount > 0 && !options.forceUpdate) {
|
||||
if (WI.settings.debugEnableStyleEditingDebugMode.value && text !== this._text)
|
||||
console.warn("Style modified while editing:", text);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow updates from the backend when text matches because `properties` may contain warnings that need to be shown.
|
||||
if (this._locked && !options.forceUpdate && text !== this._text)
|
||||
return;
|
||||
|
||||
text = text || "";
|
||||
properties = properties || [];
|
||||
|
||||
let oldProperties = this._properties || [];
|
||||
let oldText = this._text;
|
||||
|
||||
this._text = text;
|
||||
this._properties = properties;
|
||||
|
||||
this._styleSheetTextRange = styleSheetTextRange;
|
||||
this._propertyNameMap = {};
|
||||
this._variablesForType.clear();
|
||||
|
||||
this._enabledProperties = null;
|
||||
this._visibleProperties = null;
|
||||
|
||||
let editable = this.editable;
|
||||
|
||||
for (let property of this._properties) {
|
||||
property.ownerStyle = this;
|
||||
|
||||
// Store the property in a map if we aren't editable. This
|
||||
// allows for quick lookup for computed style. Editable
|
||||
// styles don't use the map since they need to account for
|
||||
// overridden properties.
|
||||
if (!editable)
|
||||
this._propertyNameMap[property.name] = property;
|
||||
else {
|
||||
// Remove from pendingProperties (if it was pending).
|
||||
this._pendingProperties.remove(property);
|
||||
}
|
||||
}
|
||||
|
||||
for (let oldProperty of oldProperties) {
|
||||
if (this._properties.includes(oldProperty))
|
||||
continue;
|
||||
|
||||
// Clear the index, since it is no longer valid.
|
||||
oldProperty.index = NaN;
|
||||
|
||||
// Keep around old properties in pending in case they
|
||||
// are needed again during editing.
|
||||
if (editable)
|
||||
this._pendingProperties.push(oldProperty);
|
||||
}
|
||||
|
||||
if (dontFireEvents)
|
||||
return;
|
||||
|
||||
// Don't fire the event if text hasn't changed. However, it should still fire for Computed style declarations
|
||||
// because it never has text.
|
||||
if (oldText === this._text && !this._pendingPropertiesChanged && this._type !== WI.CSSStyleDeclaration.Type.Computed)
|
||||
return;
|
||||
|
||||
this._pendingPropertiesChanged = false;
|
||||
|
||||
function delayed()
|
||||
{
|
||||
this.dispatchEventToListeners(WI.CSSStyleDeclaration.Event.PropertiesChanged);
|
||||
}
|
||||
|
||||
// Delay firing the PropertiesChanged event so DOMNodeStyles has a chance to mark overridden and associated properties.
|
||||
setTimeout(delayed.bind(this), 0);
|
||||
}
|
||||
|
||||
get ownerRule()
|
||||
{
|
||||
return this._ownerRule;
|
||||
}
|
||||
|
||||
set ownerRule(rule)
|
||||
{
|
||||
this._ownerRule = rule || null;
|
||||
}
|
||||
|
||||
get text()
|
||||
{
|
||||
return this._text;
|
||||
}
|
||||
|
||||
set text(text)
|
||||
{
|
||||
if (this._text === text)
|
||||
return;
|
||||
|
||||
let trimmedText = text.trim();
|
||||
if (this._text === trimmedText)
|
||||
return;
|
||||
|
||||
if (!trimmedText.length || this._type === WI.CSSStyleDeclaration.Type.Inline)
|
||||
text = trimmedText;
|
||||
|
||||
this._text = text;
|
||||
++this._updatesInProgressCount;
|
||||
|
||||
let timeoutId = setTimeout(() => {
|
||||
console.error("Timed out when setting style text:", text);
|
||||
styleTextDidChange();
|
||||
}, 2000);
|
||||
|
||||
let styleTextDidChange = () => {
|
||||
if (!timeoutId)
|
||||
return;
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
this._updatesInProgressCount = Math.max(0, this._updatesInProgressCount - 1);
|
||||
this._pendingPropertiesChanged = true;
|
||||
};
|
||||
|
||||
this._nodeStyles.changeStyleText(this, text, styleTextDidChange);
|
||||
}
|
||||
|
||||
get enabledProperties()
|
||||
{
|
||||
if (!this._enabledProperties)
|
||||
this._enabledProperties = this._properties.filter((property) => property.enabled);
|
||||
|
||||
return this._enabledProperties;
|
||||
}
|
||||
|
||||
get properties()
|
||||
{
|
||||
return this._properties;
|
||||
}
|
||||
|
||||
set properties(properties)
|
||||
{
|
||||
if (properties === this._properties)
|
||||
return;
|
||||
|
||||
this._properties = properties;
|
||||
this._enabledProperties = null;
|
||||
this._visibleProperties = null;
|
||||
}
|
||||
|
||||
get visibleProperties()
|
||||
{
|
||||
if (!this._visibleProperties)
|
||||
this._visibleProperties = this._properties.filter((property) => !!property.styleDeclarationTextRange);
|
||||
|
||||
return this._visibleProperties;
|
||||
}
|
||||
|
||||
get pendingProperties()
|
||||
{
|
||||
return this._pendingProperties;
|
||||
}
|
||||
|
||||
get styleSheetTextRange()
|
||||
{
|
||||
return this._styleSheetTextRange;
|
||||
}
|
||||
|
||||
get groupings()
|
||||
{
|
||||
if (this._ownerRule)
|
||||
return this._ownerRule.groupings;
|
||||
return [];
|
||||
}
|
||||
|
||||
get selectorText()
|
||||
{
|
||||
if (this._ownerRule)
|
||||
return this._ownerRule.selectorText;
|
||||
return this._node.appropriateSelectorFor(true);
|
||||
}
|
||||
|
||||
propertyForName(name)
|
||||
{
|
||||
console.assert(name);
|
||||
if (!name)
|
||||
return null;
|
||||
|
||||
if (!this.editable)
|
||||
return this._propertyNameMap[name] || null;
|
||||
|
||||
// Editable styles don't use the map since they need to
|
||||
// account for overridden properties.
|
||||
|
||||
let bestMatchProperty = null;
|
||||
for (let property of this.enabledProperties) {
|
||||
if (property.canonicalName !== name && property.name !== name)
|
||||
continue;
|
||||
if (bestMatchProperty && !bestMatchProperty.overridden && property.overridden)
|
||||
continue;
|
||||
bestMatchProperty = property;
|
||||
}
|
||||
|
||||
return bestMatchProperty;
|
||||
}
|
||||
|
||||
resolveVariableValue(text)
|
||||
{
|
||||
const invalid = Symbol("invalid");
|
||||
|
||||
let checkTokens = (tokens) => {
|
||||
let startIndex = NaN;
|
||||
let openParenthesis = 0;
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
let token = tokens[i];
|
||||
if (token.value === "var" && token.type && token.type.includes("atom")) {
|
||||
if (isNaN(startIndex)) {
|
||||
startIndex = i;
|
||||
openParenthesis = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isNaN(startIndex))
|
||||
continue;
|
||||
|
||||
if (token.value === "(") {
|
||||
++openParenthesis;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.value === ")") {
|
||||
--openParenthesis;
|
||||
if (openParenthesis > 0)
|
||||
continue;
|
||||
|
||||
let variableTokens = tokens.slice(startIndex, i + 1);
|
||||
startIndex = NaN;
|
||||
|
||||
let variableNameIndex = variableTokens.findIndex((token) => WI.CSSProperty.isVariable(token.value) && /\bvariable-2\b/.test(token.type));
|
||||
if (variableNameIndex === -1)
|
||||
continue;
|
||||
|
||||
let variableProperty = this.propertyForName(variableTokens[variableNameIndex].value);
|
||||
if (variableProperty)
|
||||
return variableProperty.value.trim();
|
||||
|
||||
let fallbackStartIndex = variableTokens.findIndex((value, j) => j > variableNameIndex + 1 && /\bm-css\b/.test(value.type));
|
||||
if (fallbackStartIndex === -1)
|
||||
return invalid;
|
||||
|
||||
let fallbackTokens = variableTokens.slice(fallbackStartIndex, i);
|
||||
return checkTokens(fallbackTokens) || fallbackTokens.reduce((accumulator, token) => accumulator + token.value, "").trim();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
let resolved = checkTokens(WI.tokenizeCSSValue(text));
|
||||
return resolved === invalid ? null : resolved;
|
||||
}
|
||||
|
||||
newBlankProperty(propertyIndex)
|
||||
{
|
||||
let text, name, value, priority, overridden, implicit, anonymous;
|
||||
let enabled = true;
|
||||
let valid = false;
|
||||
let styleSheetTextRange = this._rangeAfterPropertyAtIndex(propertyIndex - 1);
|
||||
|
||||
this.markModified();
|
||||
let property = new WI.CSSProperty(propertyIndex, text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange);
|
||||
property.isNewProperty = true;
|
||||
this.insertProperty(property, propertyIndex);
|
||||
this.update(this._text, this._properties, this._styleSheetTextRange, {dontFireEvents: true, forceUpdate: true});
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
markModified()
|
||||
{
|
||||
if (!this._initialState) {
|
||||
let visibleProperties = this.visibleProperties.map((property) => {
|
||||
return property.clone();
|
||||
});
|
||||
|
||||
this._initialState = new WI.CSSStyleDeclaration(
|
||||
this._nodeStyles,
|
||||
this._ownerStyleSheet,
|
||||
this._id,
|
||||
this._type,
|
||||
this._node,
|
||||
this._inherited,
|
||||
this._text,
|
||||
visibleProperties,
|
||||
this._styleSheetTextRange);
|
||||
}
|
||||
|
||||
WI.cssManager.addModifiedStyle(this);
|
||||
}
|
||||
|
||||
insertProperty(cssProperty, propertyIndex)
|
||||
{
|
||||
this._properties.insertAtIndex(cssProperty, propertyIndex);
|
||||
for (let index = propertyIndex + 1; index < this._properties.length; index++)
|
||||
this._properties[index].index = index;
|
||||
|
||||
// Invalidate cached properties.
|
||||
this._enabledProperties = null;
|
||||
this._visibleProperties = null;
|
||||
}
|
||||
|
||||
removeProperty(cssProperty)
|
||||
{
|
||||
// cssProperty.index could be set to NaN by WI.CSSStyleDeclaration.prototype.update.
|
||||
let realIndex = this._properties.indexOf(cssProperty);
|
||||
if (realIndex === -1)
|
||||
return;
|
||||
|
||||
this._properties.splice(realIndex, 1);
|
||||
|
||||
// Invalidate cached properties.
|
||||
this._enabledProperties = null;
|
||||
this._visibleProperties = null;
|
||||
}
|
||||
|
||||
updatePropertiesModifiedState()
|
||||
{
|
||||
if (!this._initialState)
|
||||
return;
|
||||
|
||||
if (this._type === WI.CSSStyleDeclaration.Type.Computed)
|
||||
return;
|
||||
|
||||
let initialCSSProperties = this._initialState.visibleProperties;
|
||||
let cssProperties = this.visibleProperties;
|
||||
|
||||
let hasModified = false;
|
||||
|
||||
function onEach(cssProperty, action) {
|
||||
if (action !== 0)
|
||||
hasModified = true;
|
||||
|
||||
cssProperty.modified = action === 1;
|
||||
}
|
||||
|
||||
function comparator(a, b) {
|
||||
return a.equals(b);
|
||||
}
|
||||
|
||||
Array.diffArrays(initialCSSProperties, cssProperties, onEach, comparator);
|
||||
|
||||
if (!hasModified)
|
||||
WI.cssManager.removeModifiedStyle(this);
|
||||
}
|
||||
|
||||
generateFormattedText(options = {})
|
||||
{
|
||||
let indentString = WI.indentString();
|
||||
let styleText = "";
|
||||
let groupings = this.groupings.filter((grouping) => !grouping.isMedia || grouping.text !== "all");
|
||||
let groupingsCount = groupings.length;
|
||||
|
||||
if (options.includeGroupingsAndSelectors) {
|
||||
for (let i = groupingsCount - 1; i >= 0; --i) {
|
||||
if (options.multiline)
|
||||
styleText += indentString.repeat(groupingsCount - i - 1);
|
||||
|
||||
let prefix = groupings[i].prefix;
|
||||
if (prefix)
|
||||
styleText += prefix;
|
||||
|
||||
if (groupings[i].text)
|
||||
styleText += " " + groupings[i].text;
|
||||
styleText += " {";
|
||||
|
||||
if (options.multiline)
|
||||
styleText += "\n";
|
||||
}
|
||||
|
||||
if (options.multiline)
|
||||
styleText += indentString.repeat(groupingsCount);
|
||||
|
||||
styleText += this.selectorText + " {";
|
||||
}
|
||||
|
||||
let properties = this._styleSheetTextRange ? this.visibleProperties : this._properties;
|
||||
if (properties.length) {
|
||||
if (options.multiline) {
|
||||
let propertyIndent = indentString.repeat(groupingsCount + 1);
|
||||
for (let property of properties)
|
||||
styleText += "\n" + propertyIndent + property.formattedText;
|
||||
|
||||
styleText += "\n";
|
||||
if (!options.includeGroupingsAndSelectors) {
|
||||
// Indent the closing "}" for nested rules.
|
||||
styleText += indentString.repeat(groupingsCount);
|
||||
}
|
||||
} else
|
||||
styleText += properties.map((property) => property.formattedText).join(" ");
|
||||
}
|
||||
|
||||
if (options.includeGroupingsAndSelectors) {
|
||||
for (let i = groupingsCount; i > 0; --i) {
|
||||
if (options.multiline)
|
||||
styleText += indentString.repeat(i);
|
||||
|
||||
styleText += "}";
|
||||
|
||||
if (options.multiline)
|
||||
styleText += "\n";
|
||||
}
|
||||
|
||||
styleText += "}";
|
||||
}
|
||||
|
||||
return styleText;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
get nodeStyles()
|
||||
{
|
||||
return this._nodeStyles;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_rangeAfterPropertyAtIndex(index)
|
||||
{
|
||||
if (index < 0)
|
||||
return this._styleSheetTextRange.collapseToStart();
|
||||
|
||||
if (index >= this.visibleProperties.length)
|
||||
return this._styleSheetTextRange.collapseToEnd();
|
||||
|
||||
let property = this.visibleProperties[index];
|
||||
return property.styleSheetTextRange.collapseToEnd();
|
||||
}
|
||||
};
|
||||
|
||||
WI.CSSStyleDeclaration.Event = {
|
||||
PropertiesChanged: "css-style-declaration-properties-changed",
|
||||
};
|
||||
|
||||
WI.CSSStyleDeclaration.Type = {
|
||||
Rule: "css-style-declaration-type-rule",
|
||||
Inline: "css-style-declaration-type-inline",
|
||||
Attribute: "css-style-declaration-type-attribute",
|
||||
Computed: "css-style-declaration-type-computed"
|
||||
};
|
||||
|
||||
WI.CSSStyleDeclaration.VariablesGroupType = {
|
||||
Ungrouped: "ungrouped",
|
||||
Colors: "colors",
|
||||
Dimensions: "dimensions",
|
||||
Numbers: "numbers",
|
||||
Other: "other",
|
||||
};
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CSSStyleSheet = class CSSStyleSheet extends WI.SourceCode
|
||||
{
|
||||
constructor(id)
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(id);
|
||||
|
||||
this._id = id || null;
|
||||
this._parentFrame = null;
|
||||
this._origin = null;
|
||||
this._startLineNumber = 0;
|
||||
this._startColumnNumber = 0;
|
||||
|
||||
this._inlineStyleAttribute = false;
|
||||
this._inlineStyleTag = false;
|
||||
|
||||
this._hasInfo = false;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static resetUniqueDisplayNameNumbers()
|
||||
{
|
||||
WI.CSSStyleSheet._nextUniqueDisplayNameNumber = 1;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get id()
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get parentFrame()
|
||||
{
|
||||
return this._parentFrame;
|
||||
}
|
||||
|
||||
get origin()
|
||||
{
|
||||
return this._origin;
|
||||
}
|
||||
|
||||
get injected()
|
||||
{
|
||||
return WI.browserManager.isExtensionScheme(this.urlComponents.scheme);
|
||||
}
|
||||
|
||||
get anonymous()
|
||||
{
|
||||
return !this.isInspectorStyleSheet() && !this._url;
|
||||
}
|
||||
|
||||
get mimeType()
|
||||
{
|
||||
return "text/css";
|
||||
}
|
||||
|
||||
get displayName()
|
||||
{
|
||||
if (this.isInspectorStyleSheet())
|
||||
return WI.UIString("Inspector Style Sheet");
|
||||
|
||||
if (this._url)
|
||||
return WI.displayNameForURL(this._url, this.urlComponents);
|
||||
|
||||
// Assign a unique number to the StyleSheet object so it will stay the same.
|
||||
if (!this._uniqueDisplayNameNumber)
|
||||
this._uniqueDisplayNameNumber = this.constructor._nextUniqueDisplayNameNumber++;
|
||||
|
||||
return WI.UIString("Anonymous Style Sheet %d").format(this._uniqueDisplayNameNumber);
|
||||
}
|
||||
|
||||
get startLineNumber()
|
||||
{
|
||||
return this._startLineNumber;
|
||||
}
|
||||
|
||||
get startColumnNumber()
|
||||
{
|
||||
return this._startColumnNumber;
|
||||
}
|
||||
|
||||
hasInfo()
|
||||
{
|
||||
return this._hasInfo;
|
||||
}
|
||||
|
||||
isInspectorStyleSheet()
|
||||
{
|
||||
return this._origin === WI.CSSStyleSheet.Type.Inspector;
|
||||
}
|
||||
|
||||
isInlineStyleTag()
|
||||
{
|
||||
return this._inlineStyleTag;
|
||||
}
|
||||
|
||||
isInlineStyleAttributeStyleSheet()
|
||||
{
|
||||
return this._inlineStyleAttribute;
|
||||
}
|
||||
|
||||
markAsInlineStyleAttributeStyleSheet()
|
||||
{
|
||||
this._inlineStyleAttribute = true;
|
||||
}
|
||||
|
||||
offsetSourceCodeLocation(sourceCodeLocation)
|
||||
{
|
||||
if (!sourceCodeLocation)
|
||||
return null;
|
||||
|
||||
if (!this._hasInfo)
|
||||
return sourceCodeLocation;
|
||||
|
||||
let sourceCode = sourceCodeLocation.sourceCode;
|
||||
let lineNumber = this._startLineNumber + sourceCodeLocation.lineNumber;
|
||||
let columnNumber = this._startColumnNumber + sourceCodeLocation.columnNumber;
|
||||
return sourceCode.createSourceCodeLocation(lineNumber, columnNumber);
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
updateInfo(url, parentFrame, origin, inlineStyle, startLineNumber, startColumnNumber)
|
||||
{
|
||||
this._hasInfo = true;
|
||||
|
||||
this._url = url || null;
|
||||
this._urlComponents = undefined;
|
||||
|
||||
this._parentFrame = parentFrame || null;
|
||||
this._origin = origin;
|
||||
|
||||
this._inlineStyleTag = inlineStyle;
|
||||
this._startLineNumber = startLineNumber;
|
||||
this._startColumnNumber = startColumnNumber;
|
||||
}
|
||||
|
||||
get revisionForRequestedContent()
|
||||
{
|
||||
return this.currentRevision;
|
||||
}
|
||||
|
||||
handleCurrentRevisionContentChange()
|
||||
{
|
||||
if (!this._id)
|
||||
return;
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
|
||||
function contentDidChange(error)
|
||||
{
|
||||
if (error)
|
||||
return;
|
||||
|
||||
if (target.hasCommand("DOM.markUndoableState"))
|
||||
target.DOMAgent.markUndoableState();
|
||||
|
||||
this.dispatchEventToListeners(WI.CSSStyleSheet.Event.ContentDidChange);
|
||||
}
|
||||
|
||||
this._ignoreNextContentDidChangeNotification = true;
|
||||
|
||||
target.CSSAgent.setStyleSheetText(this._id, this.currentRevision.content, contentDidChange.bind(this));
|
||||
}
|
||||
|
||||
requestContentFromBackend()
|
||||
{
|
||||
let specialContentPromise = WI.SourceCode.generateSpecialContentForURL(this._url);
|
||||
if (specialContentPromise)
|
||||
return specialContentPromise;
|
||||
|
||||
if (!this._id) {
|
||||
// There is no identifier to request content with. Reject the promise to cause the
|
||||
// pending callbacks to get null content.
|
||||
return Promise.reject(new Error("There is no identifier to request content with."));
|
||||
}
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
return target.CSSAgent.getStyleSheetText(this._id);
|
||||
}
|
||||
|
||||
noteContentDidChange()
|
||||
{
|
||||
if (this._ignoreNextContentDidChangeNotification) {
|
||||
this._ignoreNextContentDidChangeNotification = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.markContentAsStale();
|
||||
this.dispatchEventToListeners(WI.CSSStyleSheet.Event.ContentDidChange);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
WI.CSSStyleSheet._nextUniqueDisplayNameNumber = 1;
|
||||
|
||||
WI.CSSStyleSheet.Event = {
|
||||
ContentDidChange: "css-style-sheet-content-did-change"
|
||||
};
|
||||
|
||||
WI.CSSStyleSheet.Type = {
|
||||
Author: "css-style-sheet-type-author",
|
||||
User: "css-style-sheet-type-user",
|
||||
UserAgent: "css-style-sheet-type-user-agent",
|
||||
Inspector: "css-style-sheet-type-inspector"
|
||||
};
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CallFrame = class CallFrame
|
||||
{
|
||||
constructor(target, {id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted, blackboxed} = {})
|
||||
{
|
||||
console.assert(target instanceof WI.Target, target);
|
||||
console.assert(!sourceCodeLocation || sourceCodeLocation instanceof WI.SourceCodeLocation, sourceCodeLocation);
|
||||
console.assert(!thisObject || thisObject instanceof WI.RemoteObject, thisObject);
|
||||
console.assert(!scopeChain || scopeChain.every((item) => item instanceof WI.ScopeChainNode), scopeChain);
|
||||
|
||||
this._isConsoleEvaluation = sourceCodeLocation && isWebInspectorConsoleEvaluationScript(sourceCodeLocation.sourceCode.sourceURL);
|
||||
if (this._isConsoleEvaluation) {
|
||||
functionName = WI.UIString("Console Evaluation");
|
||||
programCode = true;
|
||||
}
|
||||
|
||||
this._target = target;
|
||||
this._id = id || null;
|
||||
this._sourceCodeLocation = sourceCodeLocation || null;
|
||||
this._functionName = functionName || "";
|
||||
this._thisObject = thisObject || null;
|
||||
this._scopeChain = scopeChain || [];
|
||||
this._nativeCode = nativeCode || false;
|
||||
this._programCode = programCode || false;
|
||||
this._isTailDeleted = isTailDeleted || false;
|
||||
this._blackboxed = blackboxed || false;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get target() { return this._target; }
|
||||
get id() { return this._id; }
|
||||
get sourceCodeLocation() { return this._sourceCodeLocation; }
|
||||
get functionName() { return this._functionName; }
|
||||
get nativeCode() { return this._nativeCode; }
|
||||
get programCode() { return this._programCode; }
|
||||
get thisObject() { return this._thisObject; }
|
||||
get scopeChain() { return this._scopeChain; }
|
||||
get isTailDeleted() { return this._isTailDeleted; }
|
||||
get blackboxed() { return this._blackboxed; }
|
||||
get isConsoleEvaluation() { return this._isConsoleEvaluation; }
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return this._functionName || WI.UIString("(anonymous function)");
|
||||
}
|
||||
|
||||
isEqual(other)
|
||||
{
|
||||
if (!other)
|
||||
return false;
|
||||
|
||||
if (this._sourceCodeLocation && other._sourceCodeLocation)
|
||||
return this._sourceCodeLocation.isEqual(other._sourceCodeLocation);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
saveIdentityToCookie()
|
||||
{
|
||||
// Do nothing. The call frame is torn down when the inspector closes, and
|
||||
// we shouldn't restore call frame content views across debugger pauses.
|
||||
}
|
||||
|
||||
collectScopeChainVariableNames(callback)
|
||||
{
|
||||
let result = ["this", "__proto__"];
|
||||
|
||||
var pendingRequests = this._scopeChain.length;
|
||||
|
||||
function propertiesCollected(properties)
|
||||
{
|
||||
for (var i = 0; properties && i < properties.length; ++i)
|
||||
result.push(properties[i].name);
|
||||
|
||||
if (--pendingRequests)
|
||||
return;
|
||||
|
||||
callback(result);
|
||||
}
|
||||
|
||||
for (var i = 0; i < this._scopeChain.length; ++i)
|
||||
this._scopeChain[i].objects[0].getPropertyDescriptors(propertiesCollected);
|
||||
}
|
||||
|
||||
mergedScopeChain()
|
||||
{
|
||||
let mergedScopes = [];
|
||||
|
||||
// Scopes list goes from top/local (1) to bottom/global (5)
|
||||
// [scope1, scope2, scope3, scope4, scope5]
|
||||
let scopes = this._scopeChain.slice();
|
||||
|
||||
// Merge similiar scopes. Some function call frames may have multiple
|
||||
// top level closure scopes (one for `var`s one for `let`s) that can be
|
||||
// combined to a single scope of variables. Go in reverse order so we
|
||||
// merge the first two closure scopes with the same name. Also mark
|
||||
// the first time we see a new name, so we know the base for the name.
|
||||
// [scope1&2, scope3, scope4, scope5]
|
||||
// foo bar GLE global
|
||||
let lastMarkedHash = null;
|
||||
function markAsBaseIfNeeded(scope) {
|
||||
if (!scope.hash)
|
||||
return false;
|
||||
if (scope.type !== WI.ScopeChainNode.Type.Closure)
|
||||
return false;
|
||||
if (scope.hash === lastMarkedHash)
|
||||
return false;
|
||||
lastMarkedHash = scope.hash;
|
||||
scope.__baseClosureScope = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
function shouldMergeClosureScopes(youngScope, oldScope, lastMerge) {
|
||||
if (!youngScope || !oldScope)
|
||||
return false;
|
||||
|
||||
// Don't merge unknown locations.
|
||||
if (!youngScope.hash || !oldScope.hash)
|
||||
return false;
|
||||
|
||||
// Only merge closure scopes.
|
||||
if (youngScope.type !== WI.ScopeChainNode.Type.Closure)
|
||||
return false;
|
||||
if (oldScope.type !== WI.ScopeChainNode.Type.Closure)
|
||||
return false;
|
||||
|
||||
// Don't merge if they are not the same.
|
||||
if (youngScope.hash !== oldScope.hash)
|
||||
return false;
|
||||
|
||||
// Don't merge if there was already a merge.
|
||||
if (lastMerge && youngScope.hash === lastMerge.hash)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
let lastScope = null;
|
||||
let lastMerge = null;
|
||||
for (let i = scopes.length - 1; i >= 0; --i) {
|
||||
let scope = scopes[i];
|
||||
markAsBaseIfNeeded(scope);
|
||||
if (shouldMergeClosureScopes(scope, lastScope, lastMerge)) {
|
||||
console.assert(lastScope.__baseClosureScope);
|
||||
let type = WI.ScopeChainNode.Type.Closure;
|
||||
let objects = lastScope.objects.concat(scope.objects);
|
||||
let merged = new WI.ScopeChainNode(type, objects, scope.name, scope.location);
|
||||
merged.__baseClosureScope = true;
|
||||
console.assert(objects.length === 2);
|
||||
|
||||
mergedScopes.pop(); // Remove the last.
|
||||
mergedScopes.push(merged); // Add the merged scope.
|
||||
|
||||
lastMerge = merged;
|
||||
lastScope = null;
|
||||
} else {
|
||||
mergedScopes.push(scope);
|
||||
|
||||
lastMerge = null;
|
||||
lastScope = scope;
|
||||
}
|
||||
}
|
||||
|
||||
mergedScopes = mergedScopes.reverse();
|
||||
|
||||
// Mark the first Closure as Local if the name matches this call frame.
|
||||
for (let scope of mergedScopes) {
|
||||
if (scope.type === WI.ScopeChainNode.Type.Closure) {
|
||||
if (scope.name === this._functionName)
|
||||
scope.convertToLocalScope();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mergedScopes;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static functionNameFromPayload(payload)
|
||||
{
|
||||
let functionName = payload.functionName;
|
||||
if (functionName === "global code")
|
||||
return WI.UIString("Global Code");
|
||||
if (functionName === "eval code")
|
||||
return WI.UIString("Eval Code");
|
||||
if (functionName === "module code")
|
||||
return WI.UIString("Module Code");
|
||||
return functionName;
|
||||
}
|
||||
|
||||
static programCodeFromPayload(payload)
|
||||
{
|
||||
return payload.functionName.endsWith(" code");
|
||||
}
|
||||
|
||||
static fromDebuggerPayload(target, payload, scopeChain, sourceCodeLocation)
|
||||
{
|
||||
return new WI.CallFrame(target, {
|
||||
id: payload.callFrameId,
|
||||
sourceCodeLocation,
|
||||
functionName: WI.CallFrame.functionNameFromPayload(payload),
|
||||
thisObject: WI.RemoteObject.fromPayload(payload.this, target),
|
||||
scopeChain,
|
||||
programCode: WI.CallFrame.programCodeFromPayload(payload),
|
||||
isTailDeleted: payload.isTailDeleted,
|
||||
blackboxed: sourceCodeLocation && !!WI.debuggerManager.blackboxDataForSourceCode(sourceCodeLocation.sourceCode),
|
||||
});
|
||||
}
|
||||
|
||||
static fromPayload(target, payload)
|
||||
{
|
||||
console.assert(payload);
|
||||
|
||||
let {url, scriptId} = payload;
|
||||
let nativeCode = false;
|
||||
let sourceCodeLocation = null;
|
||||
|
||||
if (url === "[native code]") {
|
||||
nativeCode = true;
|
||||
url = null;
|
||||
} else if (url || scriptId) {
|
||||
let sourceCode = null;
|
||||
if (scriptId) {
|
||||
sourceCode = WI.debuggerManager.scriptForIdentifier(scriptId, target);
|
||||
if (sourceCode && sourceCode.resource)
|
||||
sourceCode = sourceCode.resource;
|
||||
}
|
||||
if (!sourceCode)
|
||||
sourceCode = WI.networkManager.resourcesForURL(url).firstValue;
|
||||
if (!sourceCode)
|
||||
sourceCode = WI.debuggerManager.scriptsForURL(url, target)[0];
|
||||
|
||||
if (sourceCode) {
|
||||
// The lineNumber is 1-based, but we expect 0-based.
|
||||
let lineNumber = payload.lineNumber - 1;
|
||||
sourceCodeLocation = sourceCode.createLazySourceCodeLocation(lineNumber, payload.columnNumber);
|
||||
} else {
|
||||
// Treat this as native code if we were unable to find a source.
|
||||
console.assert(!url, "We should have detected source code for something with a url");
|
||||
nativeCode = true;
|
||||
url = null;
|
||||
}
|
||||
}
|
||||
|
||||
return new WI.CallFrame(target, {
|
||||
sourceCodeLocation,
|
||||
functionName: WI.CallFrame.functionNameFromPayload(payload),
|
||||
nativeCode,
|
||||
programCode: WI.CallFrame.programCodeFromPayload(payload),
|
||||
blackboxed: sourceCodeLocation && !!WI.debuggerManager.blackboxDataForSourceCode(sourceCodeLocation.sourceCode),
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CallingContextTree = class CallingContextTree
|
||||
{
|
||||
constructor(type)
|
||||
{
|
||||
this._type = type || WI.CallingContextTree.Type.TopDown;
|
||||
|
||||
this.reset();
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get type() { return this._type; }
|
||||
get totalNumberOfSamples() { return this._totalNumberOfSamples; }
|
||||
|
||||
reset()
|
||||
{
|
||||
this._root = new WI.CallingContextTreeNode(-1, -1, -1, "<root>", null);
|
||||
this._totalNumberOfSamples = 0;
|
||||
}
|
||||
|
||||
totalDurationInTimeRange(startTime, endTime)
|
||||
{
|
||||
return this._root.filteredTimestampsAndDuration(startTime, endTime).duration;
|
||||
}
|
||||
|
||||
updateTreeWithStackTrace({timestamp, stackFrames}, duration)
|
||||
{
|
||||
this._totalNumberOfSamples++;
|
||||
|
||||
let node = this._root;
|
||||
node.addTimestampAndExpressionLocation(timestamp, duration, null);
|
||||
|
||||
switch (this._type) {
|
||||
case WI.CallingContextTree.Type.TopDown:
|
||||
for (let i = stackFrames.length; i--; ) {
|
||||
let stackFrame = stackFrames[i];
|
||||
node = node.findOrMakeChild(stackFrame);
|
||||
node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, i === 0);
|
||||
}
|
||||
break;
|
||||
case WI.CallingContextTree.Type.BottomUp:
|
||||
for (let i = 0; i < stackFrames.length; ++i) {
|
||||
let stackFrame = stackFrames[i];
|
||||
node = node.findOrMakeChild(stackFrame);
|
||||
node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, i === 0);
|
||||
}
|
||||
break;
|
||||
case WI.CallingContextTree.Type.TopFunctionsTopDown:
|
||||
for (let i = stackFrames.length; i--; ) {
|
||||
node = this._root;
|
||||
for (let j = i + 1; j--; ) {
|
||||
let stackFrame = stackFrames[j];
|
||||
node = node.findOrMakeChild(stackFrame);
|
||||
node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, j === 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WI.CallingContextTree.Type.TopFunctionsBottomUp:
|
||||
for (let i = 0; i < stackFrames.length; i++) {
|
||||
node = this._root;
|
||||
for (let j = i; j < stackFrames.length; j++) {
|
||||
let stackFrame = stackFrames[j];
|
||||
node = node.findOrMakeChild(stackFrame);
|
||||
node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, j === 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.assert(false, "This should not be reached.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
toCPUProfilePayload(startTime, endTime)
|
||||
{
|
||||
let cpuProfile = {};
|
||||
let roots = [];
|
||||
let numSamplesInTimeRange = this._root.filteredTimestampsAndDuration(startTime, endTime).timestamps.length;
|
||||
|
||||
this._root.forEachChild((child) => {
|
||||
if (child.hasStackTraceInTimeRange(startTime, endTime))
|
||||
roots.push(child.toCPUProfileNode(numSamplesInTimeRange, startTime, endTime));
|
||||
});
|
||||
|
||||
cpuProfile.rootNodes = roots;
|
||||
return cpuProfile;
|
||||
}
|
||||
|
||||
forEachChild(callback)
|
||||
{
|
||||
this._root.forEachChild(callback);
|
||||
}
|
||||
|
||||
forEachNode(callback)
|
||||
{
|
||||
this._root.forEachNode(callback);
|
||||
}
|
||||
|
||||
// Testing.
|
||||
|
||||
static __test_makeTreeFromProtocolMessageObject(messageObject)
|
||||
{
|
||||
let tree = new WI.CallingContextTree;
|
||||
let stackTraces = messageObject.params.samples.stackTraces;
|
||||
for (let i = 0; i < stackTraces.length; i++)
|
||||
tree.updateTreeWithStackTrace(stackTraces[i]);
|
||||
return tree;
|
||||
}
|
||||
|
||||
__test_matchesStackTrace(stackTrace)
|
||||
{
|
||||
// StackTrace should have top frame first in the array and bottom frame last.
|
||||
// We don't look for a match that traces down the tree from the root; instead,
|
||||
// we match by looking at all the leafs, and matching while walking up the tree
|
||||
// towards the root. If we successfully make the walk, we've got a match that
|
||||
// suffices for a particular test. A successful match doesn't mean we actually
|
||||
// walk all the way up to the root; it just means we didn't fail while walking
|
||||
// in the direction of the root.
|
||||
let leaves = this.__test_buildLeafLinkedLists();
|
||||
|
||||
outer:
|
||||
for (let node of leaves) {
|
||||
for (let stackNode of stackTrace) {
|
||||
for (let propertyName of Object.getOwnPropertyNames(stackNode)) {
|
||||
if (stackNode[propertyName] !== node[propertyName])
|
||||
continue outer;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
__test_buildLeafLinkedLists()
|
||||
{
|
||||
let result = [];
|
||||
let parent = null;
|
||||
this._root.__test_buildLeafLinkedLists(parent, result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
WI.CallingContextTree.Type = {
|
||||
TopDown: Symbol("TopDown"),
|
||||
BottomUp: Symbol("BottomUp"),
|
||||
TopFunctionsTopDown: Symbol("TopFunctionsTopDown"),
|
||||
TopFunctionsBottomUp: Symbol("TopFunctionsBottomUp"),
|
||||
};
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CallingContextTreeNode = class CallingContextTreeNode
|
||||
{
|
||||
constructor(sourceID, line, column, name, url, hash)
|
||||
{
|
||||
this._children = {};
|
||||
this._sourceID = sourceID;
|
||||
this._line = line;
|
||||
this._column = column;
|
||||
this._name = name;
|
||||
this._url = url;
|
||||
this._uid = WI.CallingContextTreeNode.__uid++;
|
||||
|
||||
this._timestamps = [];
|
||||
this._durations = [];
|
||||
this._leafTimestamps = [];
|
||||
this._leafDurations = [];
|
||||
this._expressionLocations = {}; // Keys are "line:column" strings. Values are arrays of timestamps in sorted order.
|
||||
|
||||
this._hash = hash || WI.CallingContextTreeNode._hash(this);
|
||||
}
|
||||
|
||||
// Static and Private
|
||||
|
||||
static _hash(stackFrame)
|
||||
{
|
||||
return stackFrame.name + ":" + stackFrame.sourceID + ":" + stackFrame.line + ":" + stackFrame.column;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get sourceID() { return this._sourceID; }
|
||||
get line() { return this._line; }
|
||||
get column() { return this._column; }
|
||||
get name() { return this._name; }
|
||||
get uid() { return this._uid; }
|
||||
get url() { return this._url; }
|
||||
get hash() { return this._hash; }
|
||||
|
||||
hasChildrenInTimeRange(startTime, endTime)
|
||||
{
|
||||
for (let propertyName of Object.getOwnPropertyNames(this._children)) {
|
||||
let child = this._children[propertyName];
|
||||
if (child.hasStackTraceInTimeRange(startTime, endTime))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
hasStackTraceInTimeRange(startTime, endTime)
|
||||
{
|
||||
console.assert(startTime <= endTime);
|
||||
if (startTime > endTime)
|
||||
return false;
|
||||
|
||||
let timestamps = this._timestamps;
|
||||
let length = timestamps.length;
|
||||
if (!length)
|
||||
return false;
|
||||
|
||||
let index = timestamps.lowerBound(startTime);
|
||||
if (index === length)
|
||||
return false;
|
||||
console.assert(startTime <= timestamps[index]);
|
||||
|
||||
let hasTimestampInRange = timestamps[index] <= endTime;
|
||||
return hasTimestampInRange;
|
||||
}
|
||||
|
||||
filteredTimestampsAndDuration(startTime, endTime)
|
||||
{
|
||||
let lowerIndex = this._timestamps.lowerBound(startTime);
|
||||
let upperIndex = this._timestamps.upperBound(endTime);
|
||||
|
||||
let totalDuration = 0;
|
||||
for (let i = lowerIndex; i < upperIndex; ++i)
|
||||
totalDuration += this._durations[i];
|
||||
|
||||
return {
|
||||
timestamps: this._timestamps.slice(lowerIndex, upperIndex),
|
||||
duration: totalDuration,
|
||||
};
|
||||
}
|
||||
|
||||
filteredLeafTimestampsAndDuration(startTime, endTime)
|
||||
{
|
||||
let lowerIndex = this._leafTimestamps.lowerBound(startTime);
|
||||
let upperIndex = this._leafTimestamps.upperBound(endTime);
|
||||
|
||||
let totalDuration = 0;
|
||||
for (let i = lowerIndex; i < upperIndex; ++i)
|
||||
totalDuration += this._leafDurations[i];
|
||||
|
||||
return {
|
||||
leafTimestamps: this._leafTimestamps.slice(lowerIndex, upperIndex),
|
||||
leafDuration: totalDuration,
|
||||
};
|
||||
}
|
||||
|
||||
hasChildren()
|
||||
{
|
||||
return !isEmptyObject(this._children);
|
||||
}
|
||||
|
||||
findOrMakeChild(stackFrame)
|
||||
{
|
||||
let hash = WI.CallingContextTreeNode._hash(stackFrame);
|
||||
let node = this._children[hash];
|
||||
if (node)
|
||||
return node;
|
||||
node = new WI.CallingContextTreeNode(stackFrame.sourceID, stackFrame.line, stackFrame.column, stackFrame.name, stackFrame.url, hash);
|
||||
this._children[hash] = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
addTimestampAndExpressionLocation(timestamp, duration, expressionLocation, leaf)
|
||||
{
|
||||
console.assert(!this._timestamps.length || this._timestamps.lastValue <= timestamp, "Expected timestamps to be added in sorted, increasing, order.");
|
||||
this._timestamps.push(timestamp);
|
||||
this._durations.push(duration);
|
||||
|
||||
if (leaf) {
|
||||
this._leafTimestamps.push(timestamp);
|
||||
this._leafDurations.push(duration);
|
||||
}
|
||||
|
||||
if (!expressionLocation)
|
||||
return;
|
||||
|
||||
let {line, column} = expressionLocation;
|
||||
let hashCons = line + ":" + column;
|
||||
let timestamps = this._expressionLocations[hashCons];
|
||||
if (!timestamps) {
|
||||
timestamps = [];
|
||||
this._expressionLocations[hashCons] = timestamps;
|
||||
}
|
||||
console.assert(!timestamps.length || timestamps.lastValue <= timestamp, "Expected timestamps to be added in sorted, increasing, order.");
|
||||
timestamps.push(timestamp);
|
||||
}
|
||||
|
||||
forEachChild(callback)
|
||||
{
|
||||
for (let propertyName of Object.getOwnPropertyNames(this._children))
|
||||
callback(this._children[propertyName]);
|
||||
}
|
||||
|
||||
forEachNode(callback)
|
||||
{
|
||||
callback(this);
|
||||
this.forEachChild(function(child) {
|
||||
child.forEachNode(callback);
|
||||
});
|
||||
}
|
||||
|
||||
equals(other)
|
||||
{
|
||||
return this._hash === other.hash;
|
||||
}
|
||||
|
||||
toCPUProfileNode(numSamples, startTime, endTime)
|
||||
{
|
||||
let children = [];
|
||||
this.forEachChild((child) => {
|
||||
if (child.hasStackTraceInTimeRange(startTime, endTime))
|
||||
children.push(child.toCPUProfileNode(numSamples, startTime, endTime));
|
||||
});
|
||||
let cpuProfileNode = {
|
||||
id: this._uid,
|
||||
functionName: this._name,
|
||||
url: this._url,
|
||||
lineNumber: this._line,
|
||||
columnNumber: this._column,
|
||||
children: children
|
||||
};
|
||||
|
||||
let timestamps = [];
|
||||
let frameStartTime = Number.MAX_VALUE;
|
||||
let frameEndTime = Number.MIN_VALUE;
|
||||
for (let i = 0; i < this._timestamps.length; i++) {
|
||||
let timestamp = this._timestamps[i];
|
||||
if (startTime <= timestamp && timestamp <= endTime) {
|
||||
timestamps.push(timestamp);
|
||||
frameStartTime = Math.min(frameStartTime, timestamp);
|
||||
frameEndTime = Math.max(frameEndTime, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
cpuProfileNode.callInfo = {
|
||||
callCount: timestamps.length, // Totally not callCount, but oh well, this makes life easier because of field names.
|
||||
startTime: frameStartTime,
|
||||
endTime: frameEndTime,
|
||||
totalTime: (timestamps.length / numSamples) * (endTime - startTime)
|
||||
};
|
||||
|
||||
return cpuProfileNode;
|
||||
}
|
||||
|
||||
// Testing.
|
||||
|
||||
__test_buildLeafLinkedLists(parent, result)
|
||||
{
|
||||
let linkedListNode = {
|
||||
name: this._name,
|
||||
url: this._url,
|
||||
parent: parent
|
||||
};
|
||||
if (this.hasChildren()) {
|
||||
this.forEachChild((child) => {
|
||||
child.__test_buildLeafLinkedLists(linkedListNode, result);
|
||||
});
|
||||
} else {
|
||||
// We're a leaf.
|
||||
result.push(linkedListNode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WI.CallingContextTreeNode.__uid = 0;
|
||||
@@ -0,0 +1,494 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Canvas = class Canvas extends WI.Object
|
||||
{
|
||||
constructor(identifier, contextType, {domNode, cssCanvasName, contextAttributes, memoryCost, stackTrace} = {})
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(identifier);
|
||||
console.assert(contextType);
|
||||
console.assert(!stackTrace || stackTrace instanceof WI.StackTrace, stackTrace);
|
||||
|
||||
this._identifier = identifier;
|
||||
this._contextType = contextType;
|
||||
this._domNode = domNode || null;
|
||||
this._cssCanvasName = cssCanvasName || "";
|
||||
this._contextAttributes = contextAttributes || {};
|
||||
this._extensions = new Set;
|
||||
this._memoryCost = memoryCost || NaN;
|
||||
this._stackTrace = stackTrace || null;
|
||||
|
||||
this._clientNodes = null;
|
||||
this._shaderProgramCollection = new WI.ShaderProgramCollection;
|
||||
this._recordingCollection = new WI.RecordingCollection;
|
||||
|
||||
this._nextShaderProgramDisplayNumber = null;
|
||||
|
||||
this._requestNodePromise = null;
|
||||
|
||||
this._recordingState = WI.Canvas.RecordingState.Inactive;
|
||||
this._recordingFrames = [];
|
||||
this._recordingBufferUsed = 0;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromPayload(payload)
|
||||
{
|
||||
let contextType = null;
|
||||
switch (payload.contextType) {
|
||||
case InspectorBackend.Enum.Canvas.ContextType.Canvas2D:
|
||||
contextType = WI.Canvas.ContextType.Canvas2D;
|
||||
break;
|
||||
case InspectorBackend.Enum.Canvas.ContextType.BitmapRenderer:
|
||||
contextType = WI.Canvas.ContextType.BitmapRenderer;
|
||||
break;
|
||||
case InspectorBackend.Enum.Canvas.ContextType.WebGL:
|
||||
contextType = WI.Canvas.ContextType.WebGL;
|
||||
break;
|
||||
case InspectorBackend.Enum.Canvas.ContextType.WebGL2:
|
||||
contextType = WI.Canvas.ContextType.WebGL2;
|
||||
break;
|
||||
case InspectorBackend.Enum.Canvas.ContextType.WebGPU:
|
||||
contextType = WI.Canvas.ContextType.WebGPU;
|
||||
break;
|
||||
case InspectorBackend.Enum.Canvas.ContextType.WebMetal:
|
||||
contextType = WI.Canvas.ContextType.WebMetal;
|
||||
break;
|
||||
default:
|
||||
console.error("Invalid canvas context type", payload.contextType);
|
||||
}
|
||||
|
||||
// COMPATIBILITY (macOS 13.0, iOS 16.0): `backtrace` was renamed to `stackTrace`.
|
||||
if (payload.backtrace)
|
||||
payload.stackTrace = {callFrames: payload.backtrace};
|
||||
|
||||
return new WI.Canvas(payload.canvasId, contextType, {
|
||||
domNode: payload.nodeId ? WI.domManager.nodeForId(payload.nodeId) : null,
|
||||
cssCanvasName: payload.cssCanvasName,
|
||||
contextAttributes: payload.contextAttributes,
|
||||
memoryCost: payload.memoryCost,
|
||||
stackTrace: WI.StackTrace.fromPayload(WI.assumingMainTarget(), payload.stackTrace),
|
||||
});
|
||||
}
|
||||
|
||||
static displayNameForContextType(contextType)
|
||||
{
|
||||
switch (contextType) {
|
||||
case WI.Canvas.ContextType.Canvas2D:
|
||||
return WI.UIString("2D");
|
||||
case WI.Canvas.ContextType.BitmapRenderer:
|
||||
return WI.UIString("Bitmap Renderer", "Canvas Context Type Bitmap Renderer", "Bitmap Renderer is a type of rendering context associated with a <canvas> element");
|
||||
case WI.Canvas.ContextType.WebGL:
|
||||
return WI.unlocalizedString("WebGL");
|
||||
case WI.Canvas.ContextType.WebGL2:
|
||||
return WI.unlocalizedString("WebGL2");
|
||||
case WI.Canvas.ContextType.WebGPU:
|
||||
return WI.unlocalizedString("Web GPU");
|
||||
case WI.Canvas.ContextType.WebMetal:
|
||||
return WI.unlocalizedString("WebMetal");
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown canvas context type", contextType);
|
||||
return null;
|
||||
}
|
||||
|
||||
static displayNameForColorSpace(colorSpace)
|
||||
{
|
||||
switch(colorSpace) {
|
||||
case WI.Canvas.ColorSpace.SRGB:
|
||||
return WI.unlocalizedString("sRGB");
|
||||
case WI.Canvas.ColorSpace.DisplayP3:
|
||||
return WI.unlocalizedString("Display P3");
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown canvas color space", colorSpace);
|
||||
return null;
|
||||
}
|
||||
|
||||
static resetUniqueDisplayNameNumbers()
|
||||
{
|
||||
Canvas._nextContextUniqueDisplayNameNumber = 1;
|
||||
Canvas._nextDeviceUniqueDisplayNameNumber = 1;
|
||||
}
|
||||
|
||||
static supportsRequestContentForContextType(contextType)
|
||||
{
|
||||
switch (contextType) {
|
||||
case Canvas.ContextType.WebGPU:
|
||||
case Canvas.ContextType.WebMetal:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get identifier() { return this._identifier; }
|
||||
get contextType() { return this._contextType; }
|
||||
get cssCanvasName() { return this._cssCanvasName; }
|
||||
get contextAttributes() { return this._contextAttributes; }
|
||||
get extensions() { return this._extensions; }
|
||||
get stackTrace() { return this._stackTrace; }
|
||||
get shaderProgramCollection() { return this._shaderProgramCollection; }
|
||||
get recordingCollection() { return this._recordingCollection; }
|
||||
get recordingFrameCount() { return this._recordingFrames.length; }
|
||||
get recordingBufferUsed() { return this._recordingBufferUsed; }
|
||||
|
||||
get recordingActive()
|
||||
{
|
||||
return this._recordingState !== WI.Canvas.RecordingState.Inactive;
|
||||
}
|
||||
|
||||
get memoryCost()
|
||||
{
|
||||
return this._memoryCost;
|
||||
}
|
||||
|
||||
set memoryCost(memoryCost)
|
||||
{
|
||||
if (memoryCost === this._memoryCost)
|
||||
return;
|
||||
|
||||
this._memoryCost = memoryCost;
|
||||
|
||||
this.dispatchEventToListeners(WI.Canvas.Event.MemoryChanged);
|
||||
}
|
||||
|
||||
get displayName()
|
||||
{
|
||||
if (this._cssCanvasName)
|
||||
return WI.UIString("CSS canvas \u201C%s\u201D").format(this._cssCanvasName);
|
||||
|
||||
if (this._domNode) {
|
||||
let idSelector = this._domNode.escapedIdSelector;
|
||||
if (idSelector)
|
||||
return WI.UIString("Canvas %s").format(idSelector);
|
||||
}
|
||||
|
||||
if (this._contextType === Canvas.ContextType.WebGPU) {
|
||||
if (!this._uniqueDisplayNameNumber)
|
||||
this._uniqueDisplayNameNumber = Canvas._nextDeviceUniqueDisplayNameNumber++;
|
||||
return WI.UIString("Device %d").format(this._uniqueDisplayNameNumber);
|
||||
}
|
||||
|
||||
if (!this._uniqueDisplayNameNumber)
|
||||
this._uniqueDisplayNameNumber = Canvas._nextContextUniqueDisplayNameNumber++;
|
||||
return WI.UIString("Canvas %d").format(this._uniqueDisplayNameNumber);
|
||||
}
|
||||
|
||||
requestNode()
|
||||
{
|
||||
if (!this._requestNodePromise) {
|
||||
this._requestNodePromise = new Promise((resolve, reject) => {
|
||||
WI.domManager.ensureDocument();
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
target.CanvasAgent.requestNode(this._identifier, (error, nodeId) => {
|
||||
if (error) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
this._domNode = WI.domManager.nodeForId(nodeId);
|
||||
if (!this._domNode) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(this._domNode);
|
||||
});
|
||||
});
|
||||
}
|
||||
return this._requestNodePromise;
|
||||
}
|
||||
|
||||
requestContent()
|
||||
{
|
||||
if (!Canvas.supportsRequestContentForContextType(this._contextType))
|
||||
return Promise.resolve(null);
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
return target.CanvasAgent.requestContent(this._identifier).then((result) => result.content).catch((error) => console.error(error));
|
||||
}
|
||||
|
||||
requestClientNodes(callback)
|
||||
{
|
||||
if (this._clientNodes) {
|
||||
callback(this._clientNodes);
|
||||
return;
|
||||
}
|
||||
|
||||
WI.domManager.ensureDocument();
|
||||
|
||||
let wrappedCallback = (error, clientNodeIds) => {
|
||||
if (error) {
|
||||
callback([]);
|
||||
return;
|
||||
}
|
||||
|
||||
clientNodeIds = Array.isArray(clientNodeIds) ? clientNodeIds : [];
|
||||
this._clientNodes = clientNodeIds.map((clientNodeId) => WI.domManager.nodeForId(clientNodeId));
|
||||
callback(this._clientNodes);
|
||||
};
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
|
||||
// COMPATIBILITY (iOS 13): Canvas.requestCSSCanvasClientNodes was renamed to Canvas.requestClientNodes.
|
||||
if (!target.hasCommand("Canvas.requestClientNodes")) {
|
||||
target.CanvasAgent.requestCSSCanvasClientNodes(this._identifier, wrappedCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
target.CanvasAgent.requestClientNodes(this._identifier, wrappedCallback);
|
||||
}
|
||||
|
||||
requestSize()
|
||||
{
|
||||
function calculateSize(domNode) {
|
||||
function getAttributeValue(name) {
|
||||
let value = Number(domNode.getAttribute(name));
|
||||
if (!Number.isInteger(value) || value < 0)
|
||||
return NaN;
|
||||
return value;
|
||||
}
|
||||
|
||||
return {
|
||||
width: getAttributeValue("width"),
|
||||
height: getAttributeValue("height")
|
||||
};
|
||||
}
|
||||
|
||||
function getPropertyValue(remoteObject, name) {
|
||||
return new Promise((resolve, reject) => {
|
||||
remoteObject.getProperty(name, (error, result) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return this.requestNode().then((domNode) => {
|
||||
if (!domNode)
|
||||
return null;
|
||||
|
||||
let size = calculateSize(domNode);
|
||||
if (!isNaN(size.width) && !isNaN(size.height))
|
||||
return size;
|
||||
|
||||
// Since the "width" and "height" properties of canvas elements are more than just
|
||||
// attributes, we need to invoke the getter for each to get the actual value.
|
||||
// - https://html.spec.whatwg.org/multipage/canvas.html#attr-canvas-width
|
||||
// - https://html.spec.whatwg.org/multipage/canvas.html#attr-canvas-height
|
||||
let remoteObject = null;
|
||||
return WI.RemoteObject.resolveNode(domNode).then((object) => {
|
||||
remoteObject = object;
|
||||
return Promise.all([getPropertyValue(object, "width"), getPropertyValue(object, "height")]);
|
||||
}).then((values) => {
|
||||
let width = values[0].value;
|
||||
let height = values[1].value;
|
||||
values[0].release();
|
||||
values[1].release();
|
||||
remoteObject.release();
|
||||
return {width, height};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
startRecording(singleFrame)
|
||||
{
|
||||
let target = WI.assumingMainTarget();
|
||||
|
||||
let handleStartRecording = (error) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
this._recordingState = WI.Canvas.RecordingState.ActiveFrontend;
|
||||
|
||||
// COMPATIBILITY (iOS 12.1): Canvas.recordingStarted did not exist yet
|
||||
if (target.hasEvent("Canvas.recordingStarted"))
|
||||
return;
|
||||
|
||||
this._recordingFrames = [];
|
||||
this._recordingBufferUsed = 0;
|
||||
|
||||
this.dispatchEventToListeners(WI.Canvas.Event.RecordingStarted);
|
||||
};
|
||||
|
||||
// COMPATIBILITY (iOS 12.1): `frameCount` did not exist yet.
|
||||
if (target.hasCommand("Canvas.startRecording", "singleFrame")) {
|
||||
target.CanvasAgent.startRecording(this._identifier, singleFrame, handleStartRecording);
|
||||
return;
|
||||
}
|
||||
|
||||
if (singleFrame) {
|
||||
const frameCount = 1;
|
||||
target.CanvasAgent.startRecording(this._identifier, frameCount, handleStartRecording);
|
||||
} else
|
||||
target.CanvasAgent.startRecording(this._identifier, handleStartRecording);
|
||||
}
|
||||
|
||||
stopRecording()
|
||||
{
|
||||
let target = WI.assumingMainTarget();
|
||||
target.CanvasAgent.stopRecording(this._identifier);
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
if (this._cssCanvasName)
|
||||
cookie[WI.Canvas.CSSCanvasNameCookieKey] = this._cssCanvasName;
|
||||
else if (this._domNode)
|
||||
cookie[WI.Canvas.NodePathCookieKey] = this._domNode.path;
|
||||
|
||||
}
|
||||
|
||||
enableExtension(extension)
|
||||
{
|
||||
// Called from WI.CanvasManager.
|
||||
|
||||
this._extensions.add(extension);
|
||||
|
||||
this.dispatchEventToListeners(WI.Canvas.Event.ExtensionEnabled, {extension});
|
||||
}
|
||||
|
||||
clientNodesChanged()
|
||||
{
|
||||
// Called from WI.CanvasManager.
|
||||
|
||||
this._clientNodes = null;
|
||||
|
||||
this.dispatchEventToListeners(Canvas.Event.ClientNodesChanged);
|
||||
}
|
||||
|
||||
recordingStarted(initiator)
|
||||
{
|
||||
// Called from WI.CanvasManager.
|
||||
|
||||
if (initiator === InspectorBackend.Enum.Recording.Initiator.Console)
|
||||
this._recordingState = WI.Canvas.RecordingState.ActiveConsole;
|
||||
else if (initiator === InspectorBackend.Enum.Recording.Initiator.AutoCapture)
|
||||
this._recordingState = WI.Canvas.RecordingState.ActiveAutoCapture;
|
||||
else {
|
||||
console.assert(initiator === InspectorBackend.Enum.Recording.Initiator.Frontend);
|
||||
this._recordingState = WI.Canvas.RecordingState.ActiveFrontend;
|
||||
}
|
||||
|
||||
this._recordingFrames = [];
|
||||
this._recordingBufferUsed = 0;
|
||||
|
||||
this.dispatchEventToListeners(WI.Canvas.Event.RecordingStarted);
|
||||
}
|
||||
|
||||
recordingProgress(framesPayload, bufferUsed)
|
||||
{
|
||||
// Called from WI.CanvasManager.
|
||||
|
||||
this._recordingFrames.pushAll(framesPayload.map(WI.RecordingFrame.fromPayload));
|
||||
|
||||
this._recordingBufferUsed = bufferUsed;
|
||||
|
||||
this.dispatchEventToListeners(WI.Canvas.Event.RecordingProgress);
|
||||
}
|
||||
|
||||
recordingFinished(recordingPayload)
|
||||
{
|
||||
// Called from WI.CanvasManager.
|
||||
|
||||
let initiatedByUser = this._recordingState === WI.Canvas.RecordingState.ActiveFrontend;
|
||||
|
||||
// COMPATIBILITY (iOS 12.1): Canvas.recordingStarted did not exist yet
|
||||
if (!initiatedByUser && !InspectorBackend.hasEvent("Canvas.recordingStarted"))
|
||||
initiatedByUser = !!this.recordingActive;
|
||||
|
||||
let recording = recordingPayload ? WI.Recording.fromPayload(recordingPayload, this._recordingFrames) : null;
|
||||
if (recording) {
|
||||
recording.source = this;
|
||||
recording.createDisplayName(recordingPayload.name);
|
||||
|
||||
this._recordingCollection.add(recording);
|
||||
}
|
||||
|
||||
this._recordingState = WI.Canvas.RecordingState.Inactive;
|
||||
this._recordingFrames = [];
|
||||
this._recordingBufferUsed = 0;
|
||||
|
||||
this.dispatchEventToListeners(WI.Canvas.Event.RecordingStopped, {recording, initiatedByUser});
|
||||
}
|
||||
|
||||
nextShaderProgramDisplayNumberForProgramType(programType)
|
||||
{
|
||||
// Called from WI.ShaderProgram.
|
||||
|
||||
if (!this._nextShaderProgramDisplayNumber)
|
||||
this._nextShaderProgramDisplayNumber = {};
|
||||
|
||||
this._nextShaderProgramDisplayNumber[programType] = (this._nextShaderProgramDisplayNumber[programType] || 0) + 1;
|
||||
return this._nextShaderProgramDisplayNumber[programType];
|
||||
}
|
||||
};
|
||||
|
||||
WI.Canvas._nextContextUniqueDisplayNameNumber = 1;
|
||||
WI.Canvas._nextDeviceUniqueDisplayNameNumber = 1;
|
||||
|
||||
WI.Canvas.FrameURLCookieKey = "canvas-frame-url";
|
||||
WI.Canvas.CSSCanvasNameCookieKey = "canvas-css-canvas-name";
|
||||
|
||||
WI.Canvas.ContextType = {
|
||||
Canvas2D: "canvas-2d",
|
||||
BitmapRenderer: "bitmaprenderer",
|
||||
WebGL: "webgl",
|
||||
WebGL2: "webgl2",
|
||||
WebGPU: "webgpu",
|
||||
WebMetal: "webmetal",
|
||||
};
|
||||
|
||||
WI.Canvas.ColorSpace = {
|
||||
SRGB: "srgb",
|
||||
DisplayP3: "display-p3",
|
||||
};
|
||||
|
||||
WI.Canvas.RecordingState = {
|
||||
Inactive: "canvas-recording-state-inactive",
|
||||
ActiveFrontend: "canvas-recording-state-active-frontend",
|
||||
ActiveConsole: "canvas-recording-state-active-console",
|
||||
ActiveAutoCapture: "canvas-recording-state-active-auto-capture",
|
||||
};
|
||||
|
||||
WI.Canvas.Event = {
|
||||
MemoryChanged: "canvas-memory-changed",
|
||||
ExtensionEnabled: "canvas-extension-enabled",
|
||||
ClientNodesChanged: "canvas-client-nodes-changed",
|
||||
RecordingStarted: "canvas-recording-started",
|
||||
RecordingProgress: "canvas-recording-progress",
|
||||
RecordingStopped: "canvas-recording-stopped",
|
||||
};
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Devin Rousso <webkit@devinrousso.com>. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Collection = class Collection extends WI.Object
|
||||
{
|
||||
constructor(items = [])
|
||||
{
|
||||
super();
|
||||
|
||||
this._items = new Set;
|
||||
|
||||
for (let item of items)
|
||||
this.add(item);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get size()
|
||||
{
|
||||
return this._items.size;
|
||||
}
|
||||
|
||||
get displayName()
|
||||
{
|
||||
throw WI.NotImplementedError.subclassMustOverride();
|
||||
}
|
||||
|
||||
objectIsRequiredType(object)
|
||||
{
|
||||
throw WI.NotImplementedError.subclassMustOverride();
|
||||
}
|
||||
|
||||
add(item)
|
||||
{
|
||||
let isValidType = this.objectIsRequiredType(item);
|
||||
console.assert(isValidType);
|
||||
if (!isValidType)
|
||||
return;
|
||||
|
||||
console.assert(!this._items.has(item));
|
||||
this._items.add(item);
|
||||
|
||||
this.itemAdded(item);
|
||||
|
||||
this.dispatchEventToListeners(WI.Collection.Event.ItemAdded, {item});
|
||||
}
|
||||
|
||||
remove(item)
|
||||
{
|
||||
let wasRemoved = this._items.delete(item);
|
||||
console.assert(wasRemoved);
|
||||
|
||||
this.itemRemoved(item);
|
||||
|
||||
this.dispatchEventToListeners(WI.Collection.Event.ItemRemoved, {item});
|
||||
}
|
||||
|
||||
has(...args)
|
||||
{
|
||||
return this._items.has(...args);
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
let items = new Set(this._items);
|
||||
|
||||
this._items.clear();
|
||||
|
||||
this.itemsCleared(items);
|
||||
|
||||
for (let item of items)
|
||||
this.dispatchEventToListeners(WI.Collection.Event.ItemRemoved, {item});
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return Array.from(this);
|
||||
}
|
||||
|
||||
[Symbol.iterator]()
|
||||
{
|
||||
return this._items[Symbol.iterator]();
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
itemAdded(item)
|
||||
{
|
||||
// Implemented by subclasses.
|
||||
}
|
||||
|
||||
itemRemoved(item)
|
||||
{
|
||||
// Implemented by subclasses.
|
||||
}
|
||||
|
||||
itemsCleared(items)
|
||||
{
|
||||
// Implemented by subclasses.
|
||||
}
|
||||
};
|
||||
|
||||
WI.Collection.Event = {
|
||||
ItemAdded: "collection-item-added",
|
||||
ItemRemoved: "collection-item-removed",
|
||||
};
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CollectionEntry = class CollectionEntry
|
||||
{
|
||||
constructor(key, value)
|
||||
{
|
||||
console.assert(value instanceof WI.RemoteObject);
|
||||
console.assert(!key || key instanceof WI.RemoteObject);
|
||||
|
||||
this._key = key;
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
// Runtime.CollectionEntry.
|
||||
static fromPayload(payload, target)
|
||||
{
|
||||
if (payload.key)
|
||||
payload.key = WI.RemoteObject.fromPayload(payload.key, target);
|
||||
if (payload.value)
|
||||
payload.value = WI.RemoteObject.fromPayload(payload.value, target);
|
||||
|
||||
return new WI.CollectionEntry(payload.key, payload.value);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get key() { return this._key; }
|
||||
get value() { return this._value; }
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CollectionEntryPreview = class CollectionEntryPreview
|
||||
{
|
||||
constructor(keyPreview, valuePreview)
|
||||
{
|
||||
console.assert(valuePreview instanceof WI.ObjectPreview);
|
||||
console.assert(!keyPreview || keyPreview instanceof WI.ObjectPreview);
|
||||
|
||||
this._key = keyPreview;
|
||||
this._value = valuePreview;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
// Runtime.EntryPreview.
|
||||
static fromPayload(payload)
|
||||
{
|
||||
if (payload.key)
|
||||
payload.key = WI.ObjectPreview.fromPayload(payload.key);
|
||||
if (payload.value)
|
||||
payload.value = WI.ObjectPreview.fromPayload(payload.value);
|
||||
|
||||
return new WI.CollectionEntryPreview(payload.key, payload.value);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get keyPreview() { return this._key; }
|
||||
get valuePreview() { return this._value; }
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2017 Devin Rousso <webkit@devinrousso.com>. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.FrameCollection = class FrameCollection extends WI.Collection
|
||||
{
|
||||
// Public
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return WI.UIString("Frames");
|
||||
}
|
||||
|
||||
objectIsRequiredType(object)
|
||||
{
|
||||
return object instanceof WI.Frame;
|
||||
}
|
||||
};
|
||||
|
||||
WI.ScriptCollection = class ScriptCollection extends WI.Collection
|
||||
{
|
||||
// Public
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return WI.UIString("Scripts");
|
||||
}
|
||||
|
||||
objectIsRequiredType(object)
|
||||
{
|
||||
return object instanceof WI.Script;
|
||||
}
|
||||
};
|
||||
|
||||
WI.CSSStyleSheetCollection = class CSSStyleSheetCollection extends WI.Collection
|
||||
{
|
||||
// Public
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return WI.UIString("Style Sheets");
|
||||
}
|
||||
|
||||
objectIsRequiredType(object)
|
||||
{
|
||||
return object instanceof WI.CSSStyleSheet;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
WI.CanvasCollection = class CanvasCollection extends WI.Collection
|
||||
{
|
||||
// Public
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return WI.UIString("Canvases");
|
||||
}
|
||||
|
||||
objectIsRequiredType(object)
|
||||
{
|
||||
return object instanceof WI.Canvas;
|
||||
}
|
||||
};
|
||||
|
||||
WI.ShaderProgramCollection = class ShaderProgramCollection extends WI.Collection
|
||||
{
|
||||
// Public
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return WI.UIString("Shader Programs");
|
||||
}
|
||||
|
||||
objectIsRequiredType(object)
|
||||
{
|
||||
return object instanceof WI.ShaderProgram;
|
||||
}
|
||||
};
|
||||
|
||||
WI.RecordingCollection = class RecordingCollection extends WI.Collection
|
||||
{
|
||||
// Public
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return WI.UIString("Recordings");
|
||||
}
|
||||
|
||||
objectIsRequiredType(object)
|
||||
{
|
||||
return object instanceof WI.Recording;
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2009 Joseph Pecoraro
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.ConsoleCommandResultMessage = class ConsoleCommandResult extends WI.ConsoleMessage
|
||||
{
|
||||
constructor(target, result, wasThrown, savedResultIndex, shouldRevealConsole = true)
|
||||
{
|
||||
let source = WI.ConsoleMessage.MessageSource.JS;
|
||||
let level = wasThrown ? WI.ConsoleMessage.MessageLevel.Error : WI.ConsoleMessage.MessageLevel.Log;
|
||||
let type = WI.ConsoleMessage.MessageType.Result;
|
||||
|
||||
super(target, source, level, "", type, undefined, undefined, undefined, 0, [result], undefined, undefined);
|
||||
|
||||
this._savedResultIndex = savedResultIndex;
|
||||
this._shouldRevealConsole = shouldRevealConsole;
|
||||
|
||||
if (this._savedResultIndex && this._savedResultIndex > WI.ConsoleCommandResultMessage.maximumSavedResultIndex)
|
||||
WI.ConsoleCommandResultMessage.maximumSavedResultIndex = this._savedResultIndex;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static clearMaximumSavedResultIndex()
|
||||
{
|
||||
WI.ConsoleCommandResultMessage.maximumSavedResultIndex = 0;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get savedResultIndex()
|
||||
{
|
||||
return this._savedResultIndex;
|
||||
}
|
||||
|
||||
get shouldRevealConsole()
|
||||
{
|
||||
return this._shouldRevealConsole;
|
||||
}
|
||||
};
|
||||
|
||||
WI.ConsoleCommandResultMessage.maximumSavedResultIndex = 0;
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.ConsoleMessage = class ConsoleMessage
|
||||
{
|
||||
constructor(target, source, level, message, type, url, line, column, repeatCount, parameters, stackTrace, request, timestamp)
|
||||
{
|
||||
console.assert(target instanceof WI.Target);
|
||||
console.assert(typeof source === "string");
|
||||
console.assert(typeof level === "string");
|
||||
console.assert(typeof message === "string");
|
||||
console.assert(!type || Object.values(WI.ConsoleMessage.MessageType).includes(type));
|
||||
console.assert(!parameters || parameters.every((x) => x instanceof WI.RemoteObject));
|
||||
console.assert(!stackTrace || stackTrace instanceof WI.StackTrace, stackTrace);
|
||||
console.assert(!timestamp || !isNaN(timestamp), timestamp);
|
||||
|
||||
this._target = target;
|
||||
this._source = source;
|
||||
this._level = level;
|
||||
this._messageText = message;
|
||||
this._type = type || WI.ConsoleMessage.MessageType.Log;
|
||||
this._url = url || null;
|
||||
this._line = line || 0;
|
||||
this._column = column || 0;
|
||||
this._sourceCodeLocation = undefined;
|
||||
this._repeatCount = repeatCount || 0;
|
||||
this._parameters = parameters;
|
||||
this._stackTrace = stackTrace || null;
|
||||
this._request = request;
|
||||
this._timestamp = timestamp ?? NaN;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get target() { return this._target; }
|
||||
get source() { return this._source; }
|
||||
get level() { return this._level; }
|
||||
get messageText() { return this._messageText; }
|
||||
get type() { return this._type; }
|
||||
get url() { return this._url; }
|
||||
get line() { return this._line; }
|
||||
get column() { return this._column; }
|
||||
get repeatCount() { return this._repeatCount; }
|
||||
get parameters() { return this._parameters; }
|
||||
get stackTrace() { return this._stackTrace; }
|
||||
get request() { return this._request; }
|
||||
get timestamp() { return this._timestamp; }
|
||||
|
||||
get sourceCodeLocation()
|
||||
{
|
||||
if (this._sourceCodeLocation !== undefined)
|
||||
return this._sourceCodeLocation;
|
||||
|
||||
// First try to get the location from the top frame of the stack trace.
|
||||
let topCallFrame = this._stackTrace?.callFrames[0];
|
||||
if (topCallFrame && topCallFrame.sourceCodeLocation) {
|
||||
this._sourceCodeLocation = topCallFrame.sourceCodeLocation;
|
||||
return this._sourceCodeLocation;
|
||||
}
|
||||
|
||||
// If that doesn't exist try to get a location from the url/line/column in the ConsoleMessage.
|
||||
// FIXME <http://webkit.org/b/76404>: Remove the string equality checks for undefined once we don't get that value anymore.
|
||||
if (this._url && this._url !== "undefined") {
|
||||
let sourceCode = WI.networkManager.resourcesForURL(this._url).firstValue;
|
||||
if (sourceCode) {
|
||||
let lineNumber = this._line > 0 ? this._line - 1 : 0;
|
||||
let columnNumber = this._column > 0 ? this._column - 1 : 0;
|
||||
this._sourceCodeLocation = new WI.SourceCodeLocation(sourceCode, lineNumber, columnNumber);
|
||||
return this._sourceCodeLocation;
|
||||
}
|
||||
}
|
||||
|
||||
this._sourceCodeLocation = null;
|
||||
return this._sourceCodeLocation;
|
||||
}
|
||||
};
|
||||
|
||||
WI.ConsoleMessage.MessageSource = {
|
||||
HTML: "html",
|
||||
XML: "xml",
|
||||
JS: "javascript",
|
||||
Network: "network",
|
||||
ConsoleAPI: "console-api",
|
||||
Storage: "storage",
|
||||
Appcache: "appcache",
|
||||
Rendering: "rendering",
|
||||
CSS: "css",
|
||||
Security: "security",
|
||||
Media: "media",
|
||||
MediaSource: "mediasource",
|
||||
WebRTC: "webrtc",
|
||||
ITPDebug: "itp-debug",
|
||||
PrivateClickMeasurement: "private-click-measurement",
|
||||
PaymentRequest: "payment-request",
|
||||
Other: "other",
|
||||
|
||||
// COMPATIBILITY (iOS 14.0): `Console.ChannelSource.AdClickAttribution` was renamed to `Console.ChannelSource.PrivateClickMeasurement`.
|
||||
AdClickAttribution: "ad-click-attribution",
|
||||
};
|
||||
|
||||
WI.ConsoleMessage.MessageType = {
|
||||
Log: "log",
|
||||
Dir: "dir",
|
||||
DirXML: "dirxml",
|
||||
Table: "table",
|
||||
Trace: "trace",
|
||||
StartGroup: "startGroup",
|
||||
StartGroupCollapsed: "startGroupCollapsed",
|
||||
EndGroup: "endGroup",
|
||||
Assert: "assert",
|
||||
Timing: "timing",
|
||||
Profile: "profile",
|
||||
ProfileEnd: "profileEnd",
|
||||
Image: "image",
|
||||
Result: "result", // Frontend Only.
|
||||
};
|
||||
|
||||
WI.ConsoleMessage.MessageLevel = {
|
||||
Log: "log",
|
||||
Info: "info",
|
||||
Warning: "warning",
|
||||
Error: "error",
|
||||
Debug: "debug",
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.ConsoleSnippet = class ConsoleSnippet extends WI.LocalScript
|
||||
{
|
||||
constructor(title, source)
|
||||
{
|
||||
console.assert(typeof title === "string" && title.trim().length, title);
|
||||
|
||||
const target = null;
|
||||
const sourceURL = null;
|
||||
super(target, title, sourceURL, WI.Script.SourceType.Program, source, {editable: true});
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static createDefaultWithTitle(title)
|
||||
{
|
||||
const source = `
|
||||
/*
|
||||
* ${WI.UIString("Console Snippets are an easy way to save and evaluate JavaScript in the Console.")}
|
||||
*
|
||||
* ${WI.UIString("As such, the contents will be run as though it was typed into the Console.")}
|
||||
* ${WI.UIString("This means all of the Console Command Line API is available <https://webkit.org/web-inspector/console-command-line-api/>.")}
|
||||
*
|
||||
* ${WI.UIString("Modifications are saved automatically and will apply the next time the Console Snippet is run.")}
|
||||
* ${WI.UIString("The contents will be preserved across Web Inspector sessions.")}
|
||||
*
|
||||
* ${WI.UIString("More information is available at <https://webkit.org/web-inspector/console-snippets/>.")}
|
||||
*/
|
||||
|
||||
"Hello World!"
|
||||
`.trimStart();
|
||||
return new WI.ConsoleSnippet(title, source);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get title()
|
||||
{
|
||||
return this.url;
|
||||
}
|
||||
|
||||
run()
|
||||
{
|
||||
WI.runtimeManager.evaluateInInspectedWindow(this.content, {
|
||||
objectGroup: WI.RuntimeManager.ConsoleObjectGroup,
|
||||
includeCommandLineAPI: true,
|
||||
doNotPauseOnExceptionsAndMuteConsole: true,
|
||||
generatePreview: true,
|
||||
saveResult: true,
|
||||
}, (result, wasThrown) => {
|
||||
WI.consoleLogViewController.appendImmediateExecutionWithResult(this.title, result, {
|
||||
addSpecialUserLogClass: true,
|
||||
shouldRevealConsole: true,
|
||||
handleClick: (event) => {
|
||||
if (!WI.consoleManager.snippets.has(this))
|
||||
return;
|
||||
|
||||
const cookie = null;
|
||||
WI.showRepresentedObject(this, cookie, {
|
||||
ignoreNetworkTab: true,
|
||||
ignoreSearchTab: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static fromJSON(json)
|
||||
{
|
||||
return new WI.ConsoleSnippet(json.title, json.source);
|
||||
}
|
||||
|
||||
toJSON(key)
|
||||
{
|
||||
let json = {
|
||||
title: this.title,
|
||||
source: this.content,
|
||||
};
|
||||
if (key === WI.ObjectStore.toJSONSymbol)
|
||||
json[WI.objectStores.breakpoints.keyPath] = this.title;
|
||||
return json;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Cookie = class Cookie
|
||||
{
|
||||
constructor(type, name, value, {header, expires, session, maxAge, path, domain, secure, httpOnly, sameSite} = {})
|
||||
{
|
||||
console.assert(Object.values(WI.Cookie.Type).includes(type));
|
||||
console.assert(typeof name === "string");
|
||||
console.assert(typeof value === "string");
|
||||
console.assert(!header || typeof header === "string");
|
||||
console.assert(!expires || expires instanceof Date);
|
||||
console.assert(!session || typeof session === "boolean");
|
||||
console.assert(!maxAge || typeof maxAge === "number");
|
||||
console.assert(!path || typeof path === "string");
|
||||
console.assert(!domain || typeof domain === "string");
|
||||
console.assert(!secure || typeof secure === "boolean");
|
||||
console.assert(!httpOnly || typeof httpOnly === "boolean");
|
||||
console.assert(!sameSite || Object.values(WI.Cookie.SameSiteType).includes(sameSite));
|
||||
|
||||
this._type = type;
|
||||
this._name = name;
|
||||
this._value = value;
|
||||
this._size = this._name.length + this._value.length;
|
||||
|
||||
if (this._type === WI.Cookie.Type.Response) {
|
||||
this._header = header || "";
|
||||
this._expires = (!session && expires) || null;
|
||||
this._session = session || false;
|
||||
this._maxAge = maxAge || null;
|
||||
this._path = path || null;
|
||||
this._domain = domain || null;
|
||||
this._secure = secure || false;
|
||||
this._httpOnly = httpOnly || false;
|
||||
this._sameSite = sameSite || WI.Cookie.SameSiteType.None;
|
||||
}
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromPayload(payload)
|
||||
{
|
||||
let {name, value, ...options} = payload;
|
||||
options.expires = options.expires ? new Date(options.expires.maxDecimals(-3)) : null;
|
||||
|
||||
return new WI.Cookie(WI.Cookie.Type.Response, name, value, options);
|
||||
}
|
||||
|
||||
// RFC 6265 defines the HTTP Cookie and Set-Cookie header fields:
|
||||
// https://www.ietf.org/rfc/rfc6265.txt
|
||||
|
||||
static parseCookieRequestHeader(header)
|
||||
{
|
||||
if (!header)
|
||||
return [];
|
||||
|
||||
header = header.trim();
|
||||
if (!header)
|
||||
return [];
|
||||
|
||||
let cookies = [];
|
||||
|
||||
// Cookie: <name> = <value> ( ";" SP <name> = <value> )*?
|
||||
// NOTE: Just name/value pairs.
|
||||
|
||||
let pairs = header.split(/; /);
|
||||
for (let pair of pairs) {
|
||||
let match = pair.match(/^(?<name>[^\s=]+)[ \t]*=[ \t]*(?<value>.*)$/);
|
||||
if (!match) {
|
||||
WI.reportInternalError("Failed to parse Cookie pair", {header, pair});
|
||||
continue;
|
||||
}
|
||||
|
||||
let {name, value} = match.groups;
|
||||
cookies.push(new WI.Cookie(WI.Cookie.Type.Request, name, value));
|
||||
}
|
||||
|
||||
return cookies;
|
||||
}
|
||||
|
||||
static displayNameForSameSiteType(sameSiteType)
|
||||
{
|
||||
switch (sameSiteType) {
|
||||
case WI.Cookie.SameSiteType.None:
|
||||
return WI.unlocalizedString("None");
|
||||
case WI.Cookie.SameSiteType.Lax:
|
||||
return WI.unlocalizedString("Lax");
|
||||
case WI.Cookie.SameSiteType.Strict:
|
||||
return WI.unlocalizedString("Strict");
|
||||
default:
|
||||
console.error("Invalid SameSite type", sameSiteType);
|
||||
return sameSiteType;
|
||||
}
|
||||
}
|
||||
|
||||
// <https://httpwg.org/http-extensions/rfc6265bis.html#the-samesite-attribute-1>
|
||||
static parseSameSiteAttributeValue(attributeValue)
|
||||
{
|
||||
if (!attributeValue)
|
||||
return WI.Cookie.SameSiteType.None;
|
||||
|
||||
switch (attributeValue.toLowerCase()) {
|
||||
case "lax":
|
||||
return WI.Cookie.SameSiteType.Lax;
|
||||
case "strict":
|
||||
return WI.Cookie.SameSiteType.Strict;
|
||||
}
|
||||
|
||||
return WI.Cookie.SameSiteType.None;
|
||||
}
|
||||
|
||||
static parseSetCookieResponseHeader(header)
|
||||
{
|
||||
if (!header)
|
||||
return null;
|
||||
|
||||
// Set-Cookie: <name> = <value> ( ";" SP <attr-maybe-pair> )*?
|
||||
// NOTE: Some attributes can have pairs (e.g. "Path=/"), some are only a
|
||||
// single word (e.g. "Secure").
|
||||
|
||||
// Parse name/value.
|
||||
let nameValueMatch = header.match(/^(?<name>[^\s=]+)[ \t]*=[ \t]*(?<value>[^;]*)/);
|
||||
if (!nameValueMatch) {
|
||||
WI.reportInternalError("Failed to parse Set-Cookie header", {header});
|
||||
return null;
|
||||
}
|
||||
|
||||
let {name, value} = nameValueMatch.groups;
|
||||
let expires = null;
|
||||
let session = false;
|
||||
let maxAge = null;
|
||||
let path = null;
|
||||
let domain = null;
|
||||
let secure = false;
|
||||
let httpOnly = false;
|
||||
let sameSite = WI.Cookie.SameSiteType.None;
|
||||
|
||||
// Parse Attributes
|
||||
let remaining = header.substr(nameValueMatch[0].length);
|
||||
let attributes = remaining.split(/; ?/);
|
||||
for (let attribute of attributes) {
|
||||
if (!attribute)
|
||||
continue;
|
||||
|
||||
let match = attribute.match(/^(?<name>[^\s=]+)(?:=(?<value>.*))?$/);
|
||||
if (!match) {
|
||||
console.error("Failed to parse Set-Cookie attribute:", attribute);
|
||||
continue;
|
||||
}
|
||||
|
||||
let attributeName = match.groups.name;
|
||||
let attributeValue = match.groups.value;
|
||||
switch (attributeName.toLowerCase()) {
|
||||
case "expires":
|
||||
console.assert(attributeValue);
|
||||
expires = new Date(attributeValue);
|
||||
if (isNaN(expires.getTime())) {
|
||||
console.warn("Invalid Expires date:", attributeValue);
|
||||
expires = null;
|
||||
}
|
||||
break;
|
||||
case "max-age":
|
||||
console.assert(attributeValue);
|
||||
maxAge = parseInt(attributeValue, 10);
|
||||
if (isNaN(maxAge) || !/^\d+$/.test(attributeValue)) {
|
||||
console.warn("Invalid MaxAge value:", attributeValue);
|
||||
maxAge = null;
|
||||
}
|
||||
break;
|
||||
case "path":
|
||||
console.assert(attributeValue);
|
||||
path = attributeValue;
|
||||
break;
|
||||
case "domain":
|
||||
console.assert(attributeValue);
|
||||
domain = attributeValue;
|
||||
break;
|
||||
case "secure":
|
||||
console.assert(!attributeValue);
|
||||
secure = true;
|
||||
break;
|
||||
case "httponly":
|
||||
console.assert(!attributeValue);
|
||||
httpOnly = true;
|
||||
break;
|
||||
case "samesite":
|
||||
sameSite = WI.Cookie.parseSameSiteAttributeValue(attributeValue);
|
||||
break;
|
||||
default:
|
||||
console.warn("Unknown Cookie attribute:", attribute);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!expires)
|
||||
session = true;
|
||||
|
||||
return new WI.Cookie(WI.Cookie.Type.Response, name, value, {header, expires, session, maxAge, path, domain, secure, httpOnly, sameSite});
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get type() { return this._type; }
|
||||
get name() { return this._name; }
|
||||
get value() { return this._value; }
|
||||
get header() { return this._header; }
|
||||
get expires() { return this._expires; }
|
||||
get session() { return this._session; }
|
||||
get maxAge() { return this._maxAge; }
|
||||
get path() { return this._path; }
|
||||
get domain() { return this._domain; }
|
||||
get secure() { return this._secure; }
|
||||
get httpOnly() { return this._httpOnly; }
|
||||
get sameSite() { return this._sameSite; }
|
||||
get size() { return this._size; }
|
||||
|
||||
get url()
|
||||
{
|
||||
let url = this._secure ? "https://" : "http://";
|
||||
url += this._domain || "";
|
||||
url += this._path || "";
|
||||
return url;
|
||||
}
|
||||
|
||||
expirationDate(requestSentDate)
|
||||
{
|
||||
if (this._session)
|
||||
return null;
|
||||
|
||||
if (this._maxAge) {
|
||||
let startDate = requestSentDate || new Date;
|
||||
return new Date(startDate.getTime() + (this._maxAge * 1000));
|
||||
}
|
||||
|
||||
return this._expires;
|
||||
}
|
||||
|
||||
equals(other)
|
||||
{
|
||||
return this._type === other.type
|
||||
&& this._name === other.name
|
||||
&& this._value === other.value
|
||||
&& this._header === other.header
|
||||
&& this._expires?.getTime() === other.expires?.getTime()
|
||||
&& this._session === other.session
|
||||
&& this._maxAge === other.maxAge
|
||||
&& this._path === other.path
|
||||
&& this._domain === other.domain
|
||||
&& this._secure === other.secure
|
||||
&& this._httpOnly === other.httpOnly
|
||||
&& this._sameSite === other.sameSite;
|
||||
}
|
||||
|
||||
toProtocol()
|
||||
{
|
||||
if (typeof this._name !== "string")
|
||||
return null;
|
||||
|
||||
if (typeof this._value !== "string")
|
||||
return null;
|
||||
|
||||
if (typeof this._domain !== "string")
|
||||
return null;
|
||||
|
||||
if (typeof this._path !== "string")
|
||||
return null;
|
||||
|
||||
if (!this._session && !this._expires)
|
||||
return null;
|
||||
|
||||
if (!Object.values(WI.Cookie.SameSiteType).includes(this._sameSite))
|
||||
return null;
|
||||
|
||||
let json = {
|
||||
name: this._name,
|
||||
value: this._value,
|
||||
domain: this._domain,
|
||||
path: this._path,
|
||||
expires: this._expires?.getTime(),
|
||||
session: this._session,
|
||||
httpOnly: !!this._httpOnly,
|
||||
secure: !!this._secure,
|
||||
sameSite: this._sameSite,
|
||||
};
|
||||
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
WI.Cookie.Type = {
|
||||
Request: "request",
|
||||
Response: "response",
|
||||
};
|
||||
|
||||
// Keep these in sync with the "CookieSameSitePolicy" enum defined by the "Page" domain.
|
||||
WI.Cookie.SameSiteType = {
|
||||
None: "None",
|
||||
Lax: "Lax",
|
||||
Strict: "Strict",
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.CookieStorageObject = class CookieStorageObject
|
||||
{
|
||||
constructor(host)
|
||||
{
|
||||
this._host = host;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static cookieMatchesResourceURL(cookie, resourceURL)
|
||||
{
|
||||
var parsedURL = parseURL(resourceURL);
|
||||
if (!parsedURL || !WI.CookieStorageObject.cookieDomainMatchesResourceDomain(cookie.domain, parsedURL.host))
|
||||
return false;
|
||||
|
||||
return parsedURL.path.startsWith(cookie.path)
|
||||
&& (!cookie.port || parsedURL.port === cookie.port)
|
||||
&& (!cookie.secure || parsedURL.scheme === "https");
|
||||
}
|
||||
|
||||
static cookieDomainMatchesResourceDomain(cookieDomain, resourceDomain)
|
||||
{
|
||||
if (cookieDomain.charAt(0) !== ".")
|
||||
return resourceDomain === cookieDomain;
|
||||
return !!resourceDomain.match(new RegExp("^(?:[^\\.]+\\.)*" + cookieDomain.substring(1).escapeForRegExp() + "$"), "i");
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get host()
|
||||
{
|
||||
return this._host;
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
// FIXME <https://webkit.org/b/151413>: This class should actually store cookie data for this host.
|
||||
cookie[WI.CookieStorageObject.CookieHostCookieKey] = this.host;
|
||||
}
|
||||
};
|
||||
|
||||
WI.CookieStorageObject.TypeIdentifier = "cookie-storage";
|
||||
WI.CookieStorageObject.CookieHostCookieKey = "cookie-storage-host";
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.DOMBreakpoint = class DOMBreakpoint extends WI.Breakpoint
|
||||
{
|
||||
constructor(domNodeOrInfo, type, {disabled, actions, condition, ignoreCount, autoContinue} = {})
|
||||
{
|
||||
console.assert(domNodeOrInfo instanceof WI.DOMNode || typeof domNodeOrInfo === "object", domNodeOrInfo);
|
||||
console.assert(Object.values(WI.DOMBreakpoint.Type).includes(type), type);
|
||||
|
||||
super({disabled, actions, condition, ignoreCount, autoContinue});
|
||||
|
||||
if (domNodeOrInfo instanceof WI.DOMNode) {
|
||||
this._domNode = domNodeOrInfo;
|
||||
this._path = domNodeOrInfo.path();
|
||||
console.assert(WI.networkManager.mainFrame);
|
||||
this._url = WI.networkManager.mainFrame.url;
|
||||
} else if (domNodeOrInfo && typeof domNodeOrInfo === "object") {
|
||||
this._domNode = null;
|
||||
this._path = domNodeOrInfo.path;
|
||||
this._url = domNodeOrInfo.url;
|
||||
}
|
||||
|
||||
this._type = type;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static displayNameForType(type)
|
||||
{
|
||||
console.assert(Object.values(WI.DOMBreakpoint.Type).includes(type), type);
|
||||
|
||||
switch (type) {
|
||||
case WI.DOMBreakpoint.Type.SubtreeModified:
|
||||
return WI.UIString("Subtree Modified", "Subtree Modified @ DOM Breakpoint", "A submenu item of 'Break On' that breaks (pauses) before child DOM node is modified");
|
||||
|
||||
case WI.DOMBreakpoint.Type.AttributeModified:
|
||||
return WI.UIString("Attribute Modified", "Attribute Modified @ DOM Breakpoint", "A submenu item of 'Break On' that breaks (pauses) before DOM attribute is modified");
|
||||
|
||||
case WI.DOMBreakpoint.Type.NodeRemoved:
|
||||
return WI.UIString("Node Removed", "Node Removed @ DOM Breakpoint", "A submenu item of 'Break On' that breaks (pauses) before DOM node is removed");
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown DOM breakpoint type", type);
|
||||
return WI.UIString("DOM");
|
||||
}
|
||||
|
||||
static fromJSON(json)
|
||||
{
|
||||
return new WI.DOMBreakpoint(json, json.type, {
|
||||
disabled: json.disabled,
|
||||
condition: json.condition,
|
||||
actions: json.actions?.map((actionJSON) => WI.BreakpointAction.fromJSON(actionJSON)) || [],
|
||||
ignoreCount: json.ignoreCount,
|
||||
autoContinue: json.autoContinue,
|
||||
});
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get type() { return this._type; }
|
||||
get url() { return this._url; }
|
||||
get path() { return this._path; }
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return WI.DOMBreakpoint.displayNameForType(this._type);
|
||||
}
|
||||
|
||||
get editable()
|
||||
{
|
||||
// COMPATIBILITY (iOS 14): DOMDebugger.setDOMBreakpoint did not have an "options" parameter yet.
|
||||
return InspectorBackend.hasCommand("DOMDebugger.setDOMBreakpoint", "options");
|
||||
}
|
||||
|
||||
get domNode()
|
||||
{
|
||||
return this._domNode;
|
||||
}
|
||||
|
||||
set domNode(domNode)
|
||||
{
|
||||
console.assert(!domNode || domNode instanceof WI.DOMNode, domNode);
|
||||
console.assert(!domNode || xor(domNode, this._domNode), "domNode should not change once set", domNode, this._domNode);
|
||||
if (!xor(domNode, this._domNode))
|
||||
return;
|
||||
|
||||
this.dispatchEventToListeners(WI.DOMBreakpoint.Event.DOMNodeWillChange);
|
||||
this._domNode = domNode;
|
||||
this.dispatchEventToListeners(WI.DOMBreakpoint.Event.DOMNodeDidChange);
|
||||
}
|
||||
|
||||
remove()
|
||||
{
|
||||
super.remove();
|
||||
|
||||
WI.domDebuggerManager.removeDOMBreakpoint(this);
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie["dom-breakpoint-url"] = this._url;
|
||||
cookie["dom-breakpoint-path"] = this._path;
|
||||
cookie["dom-breakpoint-type"] = this._type;
|
||||
}
|
||||
|
||||
toJSON(key)
|
||||
{
|
||||
let json = super.toJSON(key);
|
||||
json.url = this._url;
|
||||
json.path = this._path;
|
||||
json.type = this._type;
|
||||
if (key === WI.ObjectStore.toJSONSymbol)
|
||||
json[WI.objectStores.domBreakpoints.keyPath] = this._url + ":" + this._path + ":" + this._type;
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
WI.DOMBreakpoint.Type = {
|
||||
SubtreeModified: "subtree-modified",
|
||||
AttributeModified: "attribute-modified",
|
||||
NodeRemoved: "node-removed",
|
||||
};
|
||||
|
||||
WI.DOMBreakpoint.Event = {
|
||||
DOMNodeDidChange: "dom-breakpoint-dom-node-did-change",
|
||||
DOMNodeWillChange: "dom-breakpoint-dom-node-will-change",
|
||||
};
|
||||
|
||||
WI.DOMBreakpoint.ReferencePage = WI.ReferencePage.DOMBreakpoints;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.DOMSearchMatchObject = class DOMSearchMatchObject
|
||||
{
|
||||
constructor(resource, domNode, title, searchTerm, textRange)
|
||||
{
|
||||
console.assert(resource instanceof WI.Resource);
|
||||
console.assert(domNode instanceof WI.DOMNode);
|
||||
console.assert(textRange instanceof WI.TextRange, textRange);
|
||||
|
||||
this._resource = resource;
|
||||
this._domNode = domNode;
|
||||
this._title = title;
|
||||
this._searchTerm = searchTerm;
|
||||
this._textRange = textRange;
|
||||
this._sourceCodeTextRange = null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static titleForDOMNode(domNode)
|
||||
{
|
||||
switch (domNode.nodeType()) {
|
||||
case Node.ELEMENT_NODE:
|
||||
var title = "<" + domNode.nodeNameInCorrectCase();
|
||||
for (var attribute of domNode.attributes()) {
|
||||
title += " " + attribute.name;
|
||||
if (attribute.value.length)
|
||||
title += "=\"" + attribute.value + "\"";
|
||||
}
|
||||
return title + ">";
|
||||
|
||||
case Node.TEXT_NODE:
|
||||
return "\"" + domNode.nodeValue() + "\"";
|
||||
|
||||
case Node.COMMENT_NODE:
|
||||
return "<!--" + domNode.nodeValue() + "-->";
|
||||
|
||||
case Node.DOCUMENT_TYPE_NODE:
|
||||
var title = "<!DOCTYPE " + domNode.nodeName();
|
||||
if (domNode.publicId) {
|
||||
title += " PUBLIC \"" + domNode.publicId + "\"";
|
||||
if (domNode.systemId)
|
||||
title += " \"" + domNode.systemId + "\"";
|
||||
} else if (domNode.systemId)
|
||||
title += " SYSTEM \"" + domNode.systemId + "\"";
|
||||
|
||||
return title + ">";
|
||||
|
||||
case Node.CDATA_SECTION_NODE:
|
||||
return "<![CDATA[" + domNode + "]]>";
|
||||
|
||||
case Node.PROCESSING_INSTRUCTION_NODE:
|
||||
var data = domNode.nodeValue();
|
||||
var dataString = data.length ? " " + data : "";
|
||||
var title = "<?" + domNode.nodeNameInCorrectCase() + dataString + "?>";
|
||||
return title;
|
||||
|
||||
default:
|
||||
console.error("Unknown DOM node type: ", domNode.nodeType());
|
||||
return domNode.nodeNameInCorrectCase();
|
||||
}
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get textRange() { return this._textRange; }
|
||||
|
||||
get resource()
|
||||
{
|
||||
return this._resource;
|
||||
}
|
||||
|
||||
get domNode()
|
||||
{
|
||||
return this._domNode;
|
||||
}
|
||||
|
||||
get title()
|
||||
{
|
||||
return this._title;
|
||||
}
|
||||
|
||||
get className()
|
||||
{
|
||||
if (!this._className)
|
||||
this._className = this._generateClassName();
|
||||
|
||||
return this._className;
|
||||
}
|
||||
|
||||
get searchTerm()
|
||||
{
|
||||
return this._searchTerm;
|
||||
}
|
||||
|
||||
get sourceCodeTextRange()
|
||||
{
|
||||
this._sourceCodeTextRange ??= this._resource.createSourceCodeTextRange(this._textRange);
|
||||
|
||||
return this._sourceCodeTextRange;
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie[WI.DOMSearchMatchObject.URLCookieKey] = this._resource.url.hash;
|
||||
cookie[WI.DOMSearchMatchObject.TitleKey] = this._title;
|
||||
cookie[WI.DOMSearchMatchObject.TextRangeKey] = [this._textRange.startLine, this._textRange.startColumn, this._textRange.endLine, this._textRange.endColumn].join();
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_generateClassName()
|
||||
{
|
||||
switch (this._domNode.nodeType()) {
|
||||
case Node.ELEMENT_NODE:
|
||||
return WI.DOMSearchMatchObject.DOMMatchElementIconStyleClassName;
|
||||
|
||||
case Node.TEXT_NODE:
|
||||
return WI.DOMSearchMatchObject.DOMMatchTextNodeIconStyleClassName;
|
||||
|
||||
case Node.COMMENT_NODE:
|
||||
return WI.DOMSearchMatchObject.DOMMatchCommentIconStyleClassName;
|
||||
|
||||
case Node.DOCUMENT_TYPE_NODE:
|
||||
return WI.DOMSearchMatchObject.DOMMatchDocumentTypeIconStyleClassName;
|
||||
|
||||
case Node.CDATA_SECTION_NODE:
|
||||
return WI.DOMSearchMatchObject.DOMMatchCharacterDataIconStyleClassName;
|
||||
|
||||
case Node.PROCESSING_INSTRUCTION_NODE:
|
||||
// <rdar://problem/12800950> Need icon for DOCUMENT_FRAGMENT_NODE and PROCESSING_INSTRUCTION_NODE
|
||||
return WI.DOMSearchMatchObject.DOMMatchDocumentTypeIconStyleClassName;
|
||||
|
||||
default:
|
||||
console.error("Unknown DOM node type: ", this._domNode.nodeType());
|
||||
return WI.DOMSearchMatchObject.DOMMatchNodeIconStyleClassName;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WI.DOMSearchMatchObject.DOMMatchElementIconStyleClassName = "dom-match-element-icon";
|
||||
WI.DOMSearchMatchObject.DOMMatchTextNodeIconStyleClassName = "dom-match-text-node-icon";
|
||||
WI.DOMSearchMatchObject.DOMMatchCommentIconStyleClassName = "dom-match-comment-icon";
|
||||
WI.DOMSearchMatchObject.DOMMatchDocumentTypeIconStyleClassName = "dom-match-document-type-icon";
|
||||
WI.DOMSearchMatchObject.DOMMatchCharacterDataIconStyleClassName = "dom-match-character-data-icon";
|
||||
WI.DOMSearchMatchObject.DOMMatchNodeIconStyleClassName = "dom-match-node-icon";
|
||||
|
||||
WI.DOMSearchMatchObject.TypeIdentifier = "dom-search-match-object";
|
||||
WI.DOMSearchMatchObject.URLCookieKey = "resource-url";
|
||||
WI.DOMSearchMatchObject.TitleKey = "title";
|
||||
WI.DOMSearchMatchObject.TextRangeKey = "text-range";
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.DOMStorageObject = class DOMStorageObject extends WI.Object
|
||||
{
|
||||
constructor(id, host, isLocalStorage)
|
||||
{
|
||||
super();
|
||||
|
||||
this._id = id;
|
||||
this._host = host;
|
||||
this._isLocalStorage = isLocalStorage;
|
||||
this._entries = new Map;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get id() { return this._id; }
|
||||
get host() { return this._host; }
|
||||
get entries() { return this._entries; }
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie[WI.DOMStorageObject.HostCookieKey] = this.host;
|
||||
cookie[WI.DOMStorageObject.LocalStorageCookieKey] = this.isLocalStorage();
|
||||
}
|
||||
|
||||
isLocalStorage()
|
||||
{
|
||||
return this._isLocalStorage;
|
||||
}
|
||||
|
||||
getEntries(callback)
|
||||
{
|
||||
function innerCallback(error, entries)
|
||||
{
|
||||
if (error)
|
||||
return;
|
||||
|
||||
for (let [key, value] of entries) {
|
||||
if (!key || !value)
|
||||
continue;
|
||||
|
||||
this._entries.set(key, value);
|
||||
}
|
||||
|
||||
callback(error, entries);
|
||||
}
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
target.DOMStorageAgent.getDOMStorageItems(this._id, innerCallback.bind(this));
|
||||
}
|
||||
|
||||
removeItem(key)
|
||||
{
|
||||
console.assert(this._entries.has(key));
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
return target.DOMStorageAgent.removeDOMStorageItem(this._id, key);
|
||||
}
|
||||
|
||||
setItem(key, value)
|
||||
{
|
||||
let target = WI.assumingMainTarget();
|
||||
return target.DOMStorageAgent.setDOMStorageItem(this._id, key, value);
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
let target = WI.assumingMainTarget();
|
||||
|
||||
// COMPATIBILITY (iOS 13.4): DOMStorage.clearDOMStorageItems did not exist yet.
|
||||
if (!target.hasCommand("DOMStorage.clearDOMStorageItems")) {
|
||||
let promises = [];
|
||||
for (let key of this._entries.keys())
|
||||
promises.push(this.removeItem(key));
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
return target.DOMStorageAgent.clearDOMStorageItems(this._id);
|
||||
}
|
||||
|
||||
// DOMStorageManager
|
||||
|
||||
itemsCleared()
|
||||
{
|
||||
this._entries.clear();
|
||||
|
||||
this.dispatchEventToListeners(WI.DOMStorageObject.Event.ItemsCleared);
|
||||
}
|
||||
|
||||
itemRemoved(key)
|
||||
{
|
||||
let removed = this._entries.delete(key);
|
||||
console.assert(removed);
|
||||
|
||||
this.dispatchEventToListeners(WI.DOMStorageObject.Event.ItemRemoved, {key});
|
||||
}
|
||||
|
||||
itemAdded(key, value)
|
||||
{
|
||||
console.assert(!this._entries.has(key));
|
||||
this._entries.set(key, value);
|
||||
|
||||
this.dispatchEventToListeners(WI.DOMStorageObject.Event.ItemAdded, {key, value});
|
||||
}
|
||||
|
||||
itemUpdated(key, oldValue, newValue)
|
||||
{
|
||||
console.assert(this._entries.get(key) === oldValue);
|
||||
this._entries.set(key, newValue);
|
||||
|
||||
this.dispatchEventToListeners(WI.DOMStorageObject.Event.ItemUpdated, {key, oldValue, newValue});
|
||||
}
|
||||
};
|
||||
|
||||
WI.DOMStorageObject.TypeIdentifier = "dom-storage";
|
||||
WI.DOMStorageObject.HostCookieKey = "dom-storage-object-host";
|
||||
WI.DOMStorageObject.LocalStorageCookieKey = "dom-storage-object-local-storage";
|
||||
|
||||
WI.DOMStorageObject.Event = {
|
||||
ItemsCleared: "dom-storage-object-items-cleared",
|
||||
ItemAdded: "dom-storage-object-item-added",
|
||||
ItemRemoved: "dom-storage-object-item-removed",
|
||||
ItemUpdated: "dom-storage-object-updated",
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.DOMStyleable = class DOMStyleable
|
||||
{
|
||||
constructor(node, {pseudoId} = {})
|
||||
{
|
||||
console.assert(node instanceof WI.DOMNode, node);
|
||||
console.assert(!pseudoId || Object.values(WI.CSSManager.PseudoSelectorNames).includes(pseudoId), pseudoId);
|
||||
|
||||
this._node = node;
|
||||
this._pseudoId = pseudoId || null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromPayload({nodeId, pseudoId})
|
||||
{
|
||||
return new WI.DOMStyleable(WI.domManager.nodeForId(nodeId), {pseudoId});
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get node() { return this._node; }
|
||||
get pseudoId() { return this._pseudoId; }
|
||||
|
||||
get displayName()
|
||||
{
|
||||
if (this._pseudoId)
|
||||
return WI.CSSManager.displayNameForPseudoId(this._pseudoId);
|
||||
return this._node.displayName;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.DOMTree = class DOMTree extends WI.Object
|
||||
{
|
||||
constructor(frame)
|
||||
{
|
||||
super();
|
||||
|
||||
this._frame = frame;
|
||||
|
||||
this._rootDOMNode = null;
|
||||
this._requestIdentifier = 0;
|
||||
|
||||
this._frame.addEventListener(WI.Frame.Event.PageExecutionContextChanged, this._framePageExecutionContextChanged, this);
|
||||
|
||||
WI.domManager.addEventListener(WI.DOMManager.Event.DocumentUpdated, this._documentUpdated, this);
|
||||
|
||||
// Only add extra event listeners when not the main frame. Since DocumentUpdated is enough for the main frame.
|
||||
if (!this._frame.isMainFrame()) {
|
||||
WI.domManager.addEventListener(WI.DOMManager.Event.NodeRemoved, this._nodeRemoved, this);
|
||||
this._frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._frameMainResourceDidChange, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get frame() { return this._frame; }
|
||||
|
||||
disconnect()
|
||||
{
|
||||
this._frame.removeEventListener(WI.Frame.Event.PageExecutionContextChanged, this._framePageExecutionContextChanged, this);
|
||||
|
||||
WI.domManager.removeEventListener(WI.DOMManager.Event.DocumentUpdated, this._documentUpdated, this);
|
||||
|
||||
if (!this._frame.isMainFrame()) {
|
||||
WI.domManager.removeEventListener(WI.DOMManager.Event.NodeRemoved, this._nodeRemoved, this);
|
||||
this._frame.removeEventListener(WI.Frame.Event.MainResourceDidChange, this._frameMainResourceDidChange, this);
|
||||
}
|
||||
}
|
||||
|
||||
invalidate()
|
||||
{
|
||||
// Set to null so it is fetched again next time requestRootDOMNode is called.
|
||||
this._rootDOMNode = null;
|
||||
|
||||
// Clear the pending callbacks. It is the responsibility of the client to listen for
|
||||
// the RootDOMNodeInvalidated event and request the root DOM node again.
|
||||
this._pendingRootDOMNodeRequests = null;
|
||||
|
||||
if (this._invalidateTimeoutIdentifier)
|
||||
return;
|
||||
|
||||
function performInvalidate()
|
||||
{
|
||||
this._invalidateTimeoutIdentifier = undefined;
|
||||
|
||||
this.dispatchEventToListeners(WI.DOMTree.Event.RootDOMNodeInvalidated);
|
||||
}
|
||||
|
||||
// Delay the invalidation on a timeout to coalesce multiple calls to invalidate.
|
||||
this._invalidateTimeoutIdentifier = setTimeout(performInvalidate.bind(this), 0);
|
||||
}
|
||||
|
||||
requestRootDOMNode(callback)
|
||||
{
|
||||
console.assert(typeof callback === "function");
|
||||
if (typeof callback !== "function")
|
||||
return;
|
||||
|
||||
if (this._rootDOMNode) {
|
||||
callback(this._rootDOMNode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._frame.isMainFrame() && !this._frame.pageExecutionContext) {
|
||||
this._rootDOMNodeRequestWaitingForExecutionContext = true;
|
||||
if (!this._pendingRootDOMNodeRequests)
|
||||
this._pendingRootDOMNodeRequests = [];
|
||||
this._pendingRootDOMNodeRequests.push(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._pendingRootDOMNodeRequests) {
|
||||
this._pendingRootDOMNodeRequests.push(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
this._pendingRootDOMNodeRequests = [callback];
|
||||
this._requestRootDOMNode();
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_requestRootDOMNode()
|
||||
{
|
||||
console.assert(this._frame.isMainFrame() || this._frame.pageExecutionContext);
|
||||
console.assert(this._pendingRootDOMNodeRequests.length);
|
||||
|
||||
// Bump the request identifier. This prevents pending callbacks for previous requests from completing.
|
||||
var requestIdentifier = ++this._requestIdentifier;
|
||||
|
||||
function rootObjectAvailable(error, result)
|
||||
{
|
||||
// Check to see if we have been invalidated (if the callbacks were cleared).
|
||||
if (!this._pendingRootDOMNodeRequests || requestIdentifier !== this._requestIdentifier)
|
||||
return;
|
||||
|
||||
if (error) {
|
||||
console.error(JSON.stringify(error));
|
||||
|
||||
this._rootDOMNode = null;
|
||||
dispatchCallbacks.call(this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert the RemoteObject to a DOMNode by asking the backend to push it to us.
|
||||
var remoteObject = WI.RemoteObject.fromPayload(result);
|
||||
remoteObject.pushNodeToFrontend(rootDOMNodeAvailable.bind(this, remoteObject));
|
||||
}
|
||||
|
||||
function rootDOMNodeAvailable(remoteObject, nodeId)
|
||||
{
|
||||
remoteObject.release();
|
||||
|
||||
// Check to see if we have been invalidated (if the callbacks were cleared).
|
||||
if (!this._pendingRootDOMNodeRequests || requestIdentifier !== this._requestIdentifier)
|
||||
return;
|
||||
|
||||
if (!nodeId) {
|
||||
this._rootDOMNode = null;
|
||||
dispatchCallbacks.call(this);
|
||||
return;
|
||||
}
|
||||
|
||||
this._rootDOMNode = WI.domManager.nodeForId(nodeId);
|
||||
|
||||
console.assert(this._rootDOMNode);
|
||||
if (!this._rootDOMNode) {
|
||||
dispatchCallbacks.call(this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Request the child nodes since the root node is often not shown in the UI,
|
||||
// and the child nodes will be needed immediately.
|
||||
this._rootDOMNode.getChildNodes(dispatchCallbacks.bind(this));
|
||||
}
|
||||
|
||||
function mainDocumentAvailable(document)
|
||||
{
|
||||
this._rootDOMNode = document;
|
||||
|
||||
dispatchCallbacks.call(this);
|
||||
}
|
||||
|
||||
function dispatchCallbacks()
|
||||
{
|
||||
// Check to see if we have been invalidated (if the callbacks were cleared).
|
||||
if (!this._pendingRootDOMNodeRequests || requestIdentifier !== this._requestIdentifier)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < this._pendingRootDOMNodeRequests.length; ++i)
|
||||
this._pendingRootDOMNodeRequests[i](this._rootDOMNode);
|
||||
this._pendingRootDOMNodeRequests = null;
|
||||
}
|
||||
|
||||
// For the main frame we can use the more straight forward requestDocument function. For
|
||||
// child frames we need to do a more roundabout approach since the protocol does not include
|
||||
// a specific way to request a document given a frame identifier. The child frame approach
|
||||
// involves evaluating the JavaScript "document" and resolving that into a DOMNode.
|
||||
if (this._frame.isMainFrame())
|
||||
WI.domManager.requestDocument(mainDocumentAvailable.bind(this));
|
||||
else {
|
||||
let target = WI.assumingMainTarget();
|
||||
var contextId = this._frame.pageExecutionContext.id;
|
||||
target.RuntimeAgent.evaluate.invoke({expression: appendWebInspectorSourceURL("document"), objectGroup: "", includeCommandLineAPI: false, doNotPauseOnExceptionsAndMuteConsole: true, contextId, returnByValue: false, generatePreview: false}, rootObjectAvailable.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
_nodeRemoved(event)
|
||||
{
|
||||
console.assert(!this._frame.isMainFrame());
|
||||
|
||||
if (event.data.node !== this._rootDOMNode)
|
||||
return;
|
||||
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
_documentUpdated(event)
|
||||
{
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
_frameMainResourceDidChange(event)
|
||||
{
|
||||
console.assert(!this._frame.isMainFrame());
|
||||
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
_framePageExecutionContextChanged(event)
|
||||
{
|
||||
if (this._rootDOMNodeRequestWaitingForExecutionContext) {
|
||||
console.assert(this._frame.pageExecutionContext);
|
||||
console.assert(this._pendingRootDOMNodeRequests && this._pendingRootDOMNodeRequests.length);
|
||||
|
||||
this._rootDOMNodeRequestWaitingForExecutionContext = false;
|
||||
|
||||
this._requestRootDOMNode();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WI.DOMTree.Event = {
|
||||
RootDOMNodeInvalidated: "dom-tree-root-dom-node-invalidated",
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.DatabaseObject = class DatabaseObject
|
||||
{
|
||||
constructor(id, host, name, version)
|
||||
{
|
||||
this._id = id;
|
||||
this._host = host ? host : WI.UIString("Local File");
|
||||
this._name = name;
|
||||
this._version = version;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get id() { return this._id; }
|
||||
get host() { return this._host; }
|
||||
get name() { return this._name; }
|
||||
get version() { return this._version; }
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie[WI.DatabaseObject.HostCookieKey] = this.host;
|
||||
cookie[WI.DatabaseObject.NameCookieKey] = this.name;
|
||||
}
|
||||
|
||||
getTableNames(callback)
|
||||
{
|
||||
function sortingCallback(error, names)
|
||||
{
|
||||
if (!error)
|
||||
callback(names.sort());
|
||||
}
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
target.DatabaseAgent.getDatabaseTableNames(this._id, sortingCallback);
|
||||
}
|
||||
|
||||
executeSQL(query, successCallback, errorCallback)
|
||||
{
|
||||
function queryCallback(error, columnNames, values, sqlError)
|
||||
{
|
||||
if (error) {
|
||||
errorCallback(WI.UIString("An unexpected error occurred."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sqlError) {
|
||||
switch (sqlError.code) {
|
||||
case SQLError.VERSION_ERR:
|
||||
errorCallback(WI.UIString("Database no longer has expected version."));
|
||||
break;
|
||||
case SQLError.TOO_LARGE_ERR:
|
||||
errorCallback(WI.UIString("Data returned from the database is too large."));
|
||||
break;
|
||||
default:
|
||||
errorCallback(WI.UIString("An unexpected error occurred."));
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
successCallback(columnNames, values);
|
||||
}
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
target.DatabaseAgent.executeSQL(this._id, query, queryCallback);
|
||||
}
|
||||
};
|
||||
|
||||
WI.DatabaseObject.TypeIdentifier = "database";
|
||||
WI.DatabaseObject.HostCookieKey = "database-object-host";
|
||||
WI.DatabaseObject.NameCookieKey = "database-object-name";
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.DatabaseTableObject = class DatabaseTableObject
|
||||
{
|
||||
constructor(name, database)
|
||||
{
|
||||
console.assert(database instanceof WI.DatabaseObject);
|
||||
|
||||
this._name = name;
|
||||
this._database = database;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get name() { return this._name; }
|
||||
get database() { return this._database; }
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie[WI.DatabaseTableObject.NameCookieKey] = this.name;
|
||||
}
|
||||
};
|
||||
|
||||
WI.DatabaseTableObject.TypeIdentifier = "database-table";
|
||||
WI.DatabaseTableObject.NameCookieKey = "database-table-object-name";
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.DebuggerDashboard = class DebuggerDashboard
|
||||
{
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.DebuggerData = class DebuggerData
|
||||
{
|
||||
constructor(target)
|
||||
{
|
||||
console.assert(target instanceof WI.Target);
|
||||
|
||||
this._target = target;
|
||||
|
||||
this._paused = false;
|
||||
this._pausing = false;
|
||||
this._pauseReason = null;
|
||||
this._pauseData = null;
|
||||
this._stackTrace = null;
|
||||
|
||||
this._scriptIdMap = new Map;
|
||||
this._scriptContentIdentifierMap = new Map;
|
||||
|
||||
this._makePausingAfterNextResume = false;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get target() { return this._target; }
|
||||
get paused() { return this._paused; }
|
||||
get pausing() { return this._pausing; }
|
||||
get pauseReason() { return this._pauseReason; }
|
||||
get pauseData() { return this._pauseData; }
|
||||
get stackTrace() { return this._stackTrace; }
|
||||
|
||||
get scripts()
|
||||
{
|
||||
return Array.from(this._scriptIdMap.values());
|
||||
}
|
||||
|
||||
scriptForIdentifier(id)
|
||||
{
|
||||
return this._scriptIdMap.get(id);
|
||||
}
|
||||
|
||||
scriptsForURL(url)
|
||||
{
|
||||
return this._scriptContentIdentifierMap.get(url) || [];
|
||||
}
|
||||
|
||||
// Protected (Called by DebuggerManager)
|
||||
|
||||
reset()
|
||||
{
|
||||
this._scriptIdMap.clear();
|
||||
}
|
||||
|
||||
addScript(script)
|
||||
{
|
||||
this._scriptIdMap.set(script.id, script);
|
||||
|
||||
if (script.contentIdentifier) {
|
||||
let scripts = this._scriptContentIdentifierMap.get(script.contentIdentifier);
|
||||
if (!scripts) {
|
||||
scripts = [];
|
||||
this._scriptContentIdentifierMap.set(script.contentIdentifier, scripts);
|
||||
}
|
||||
scripts.push(script);
|
||||
}
|
||||
}
|
||||
|
||||
pauseIfNeeded()
|
||||
{
|
||||
if (this._paused || this._pausing)
|
||||
return Promise.resolve();
|
||||
|
||||
this._pausing = true;
|
||||
|
||||
return this._target.DebuggerAgent.pause();
|
||||
}
|
||||
|
||||
resumeIfNeeded()
|
||||
{
|
||||
if (!this._paused && !this._pausing)
|
||||
return Promise.resolve();
|
||||
|
||||
this._pausing = false;
|
||||
|
||||
return this._target.DebuggerAgent.resume();
|
||||
}
|
||||
|
||||
continueUntilNextRunLoop()
|
||||
{
|
||||
if (!this._paused || this._pausing)
|
||||
return Promise.resolve();
|
||||
|
||||
// The backend will automatically start pausing
|
||||
// after resuming, so we need to match that here.
|
||||
this._makePausingAfterNextResume = true;
|
||||
|
||||
return this._target.DebuggerAgent.continueUntilNextRunLoop();
|
||||
}
|
||||
|
||||
updateForPause(stackTrace, pauseReason, pauseData)
|
||||
{
|
||||
this._paused = true;
|
||||
this._pausing = false;
|
||||
this._pauseReason = pauseReason;
|
||||
this._pauseData = pauseData;
|
||||
this._stackTrace = stackTrace;
|
||||
|
||||
// We paused, no need for auto-pausing.
|
||||
this._makePausingAfterNextResume = false;
|
||||
}
|
||||
|
||||
updateForResume()
|
||||
{
|
||||
this._paused = false;
|
||||
this._pausing = false;
|
||||
this._pauseReason = null;
|
||||
this._pauseData = null;
|
||||
this._stackTrace = null;
|
||||
|
||||
// We resumed, but may be auto-pausing.
|
||||
if (this._makePausingAfterNextResume) {
|
||||
this._makePausingAfterNextResume = false;
|
||||
this._pausing = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.DefaultDashboard = class DefaultDashboard extends WI.Object
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
|
||||
this._waitingForFirstMainResourceToStartTrackingSize = true;
|
||||
|
||||
// Necessary event required to track page load time and resource sizes.
|
||||
WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
|
||||
WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this);
|
||||
|
||||
// Necessary events required to track load of resources.
|
||||
WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
|
||||
WI.Target.addEventListener(WI.Target.Event.ResourceAdded, this._resourceWasAdded, this);
|
||||
WI.networkManager.addEventListener(WI.NetworkManager.Event.FrameWasAdded, this._frameWasAdded, this);
|
||||
|
||||
// Necessary events required to track console messages.
|
||||
WI.consoleManager.addEventListener(WI.ConsoleManager.Event.Cleared, this._consoleWasCleared, this);
|
||||
WI.consoleManager.addEventListener(WI.ConsoleManager.Event.MessageAdded, this._consoleMessageAdded, this);
|
||||
WI.consoleManager.addEventListener(WI.ConsoleManager.Event.PreviousMessageRepeatCountUpdated, this._consoleMessageWasRepeated, this);
|
||||
|
||||
// FIXME: This is working around the order of events. Normal page navigation
|
||||
// triggers a MainResource change and then a MainFrame change. Page Transition
|
||||
// triggers a MainFrame change then a MainResource change.
|
||||
this._transitioningPageTarget = false;
|
||||
|
||||
WI.notifications.addEventListener(WI.Notification.TransitionPageTarget, this._transitionPageTarget, this);
|
||||
|
||||
this._resourcesCount = 0;
|
||||
this._resourcesSize = 0;
|
||||
this._time = 0;
|
||||
this._logs = 0;
|
||||
this._errors = 0;
|
||||
this._issues = 0;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get resourcesCount()
|
||||
{
|
||||
return this._resourcesCount;
|
||||
}
|
||||
|
||||
set resourcesCount(value)
|
||||
{
|
||||
this._resourcesCount = value;
|
||||
this._dataDidChange();
|
||||
}
|
||||
|
||||
get resourcesSize()
|
||||
{
|
||||
return this._resourcesSize;
|
||||
}
|
||||
|
||||
set resourcesSize(value)
|
||||
{
|
||||
this._resourcesSize = value;
|
||||
this._dataDidChange();
|
||||
}
|
||||
|
||||
get time()
|
||||
{
|
||||
return this._time;
|
||||
}
|
||||
|
||||
set time(value)
|
||||
{
|
||||
this._time = value;
|
||||
this._dataDidChange();
|
||||
}
|
||||
|
||||
get logs()
|
||||
{
|
||||
return this._logs;
|
||||
}
|
||||
|
||||
set logs(value)
|
||||
{
|
||||
this._logs = value;
|
||||
this._dataDidChange();
|
||||
}
|
||||
|
||||
get errors()
|
||||
{
|
||||
return this._errors;
|
||||
}
|
||||
|
||||
set errors(value)
|
||||
{
|
||||
this._errors = value;
|
||||
this._dataDidChange();
|
||||
}
|
||||
|
||||
get issues()
|
||||
{
|
||||
return this._issues;
|
||||
}
|
||||
|
||||
set issues(value)
|
||||
{
|
||||
this._issues = value;
|
||||
this._dataDidChange();
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_dataDidChange()
|
||||
{
|
||||
this.dispatchEventToListeners(WI.DefaultDashboard.Event.DataDidChange);
|
||||
}
|
||||
|
||||
_mainResourceDidChange(event)
|
||||
{
|
||||
console.assert(event.target instanceof WI.Frame);
|
||||
|
||||
if (!event.target.isMainFrame())
|
||||
return;
|
||||
|
||||
if (!this._transitioningPageTarget) {
|
||||
this._time = 0;
|
||||
this._resourcesCount = 1;
|
||||
this._resourcesSize = WI.networkManager.mainFrame.mainResource.size || 0;
|
||||
}
|
||||
|
||||
// We should only track resource sizes on fresh loads.
|
||||
if (this._waitingForFirstMainResourceToStartTrackingSize) {
|
||||
this._waitingForFirstMainResourceToStartTrackingSize = false;
|
||||
WI.Resource.addEventListener(WI.Resource.Event.SizeDidChange, this._resourceSizeDidChange, this);
|
||||
}
|
||||
|
||||
this._dataDidChange();
|
||||
|
||||
if (!this._transitioningPageTarget)
|
||||
this._startUpdatingTime();
|
||||
|
||||
if (this._transitioningPageTarget)
|
||||
this._transitioningPageTarget = false;
|
||||
}
|
||||
|
||||
_handleTimelineCapturingStateChanged(event)
|
||||
{
|
||||
if (WI.timelineManager.isCapturing())
|
||||
return;
|
||||
|
||||
// If recording stops, we should stop the timer if it hasn't stopped already.
|
||||
this._stopUpdatingTime();
|
||||
}
|
||||
|
||||
_resourceWasAdded(event)
|
||||
{
|
||||
++this.resourcesCount;
|
||||
}
|
||||
|
||||
_frameWasAdded(event)
|
||||
{
|
||||
++this.resourcesCount;
|
||||
}
|
||||
|
||||
_resourceSizeDidChange(event)
|
||||
{
|
||||
if (event.target.urlComponents.scheme === "data")
|
||||
return;
|
||||
|
||||
let delta = event.target.size - event.data.previousSize;
|
||||
console.assert(!isNaN(delta), "Resource size change should never be NaN.");
|
||||
this.resourcesSize += delta;
|
||||
}
|
||||
|
||||
_startUpdatingTime()
|
||||
{
|
||||
this._stopUpdatingTime();
|
||||
|
||||
this.time = 0;
|
||||
|
||||
this._timelineBaseTime = Date.now();
|
||||
this._timeIntervalDelay = 50;
|
||||
this._timeIntervalIdentifier = setInterval(this._updateTime.bind(this), this._timeIntervalDelay);
|
||||
}
|
||||
|
||||
_stopUpdatingTime()
|
||||
{
|
||||
if (!this._timeIntervalIdentifier)
|
||||
return;
|
||||
|
||||
clearInterval(this._timeIntervalIdentifier);
|
||||
this._timeIntervalIdentifier = undefined;
|
||||
}
|
||||
|
||||
_updateTime()
|
||||
{
|
||||
var duration = Date.now() - this._timelineBaseTime;
|
||||
|
||||
var timeIntervalDelay = this._timeIntervalDelay;
|
||||
if (duration >= 1000) // 1 second
|
||||
timeIntervalDelay = 100;
|
||||
else if (duration >= 60000) // 60 seconds
|
||||
timeIntervalDelay = 1000;
|
||||
else if (duration >= 3600000) // 1 minute
|
||||
timeIntervalDelay = 10000;
|
||||
|
||||
if (timeIntervalDelay !== this._timeIntervalDelay) {
|
||||
this._timeIntervalDelay = timeIntervalDelay;
|
||||
|
||||
clearInterval(this._timeIntervalIdentifier);
|
||||
this._timeIntervalIdentifier = setInterval(this._updateTime.bind(this), this._timeIntervalDelay);
|
||||
}
|
||||
|
||||
var mainFrame = WI.networkManager.mainFrame;
|
||||
var mainFrameStartTime = mainFrame.mainResource.firstTimestamp;
|
||||
var mainFrameLoadEventTime = mainFrame.loadEventTimestamp;
|
||||
|
||||
if (isNaN(mainFrameStartTime) || isNaN(mainFrameLoadEventTime)) {
|
||||
this.time = duration / 1000;
|
||||
return;
|
||||
}
|
||||
|
||||
this.time = mainFrameLoadEventTime - mainFrameStartTime;
|
||||
|
||||
this._stopUpdatingTime();
|
||||
}
|
||||
|
||||
_consoleMessageAdded(event)
|
||||
{
|
||||
var message = event.data.message;
|
||||
this._lastConsoleMessageType = message.level;
|
||||
this._incrementConsoleMessageType(message.level, message.repeatCount);
|
||||
}
|
||||
|
||||
_consoleMessageWasRepeated(event)
|
||||
{
|
||||
this._incrementConsoleMessageType(this._lastConsoleMessageType, 1);
|
||||
}
|
||||
|
||||
_incrementConsoleMessageType(type, increment)
|
||||
{
|
||||
switch (type) {
|
||||
case WI.ConsoleMessage.MessageLevel.Log:
|
||||
case WI.ConsoleMessage.MessageLevel.Info:
|
||||
case WI.ConsoleMessage.MessageLevel.Debug:
|
||||
this.logs += increment;
|
||||
break;
|
||||
case WI.ConsoleMessage.MessageLevel.Warning:
|
||||
this.issues += increment;
|
||||
break;
|
||||
case WI.ConsoleMessage.MessageLevel.Error:
|
||||
this.errors += increment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_consoleWasCleared(event)
|
||||
{
|
||||
this._logs = 0;
|
||||
this._issues = 0;
|
||||
this._errors = 0;
|
||||
this._dataDidChange();
|
||||
}
|
||||
|
||||
_transitionPageTarget()
|
||||
{
|
||||
this._transitioningPageTarget = true;
|
||||
|
||||
this._time = 0;
|
||||
this._resourcesCount = 0;
|
||||
this._resourcesSize = 0;
|
||||
|
||||
this._dataDidChange();
|
||||
}
|
||||
};
|
||||
|
||||
WI.DefaultDashboard.Event = {
|
||||
DataDidChange: "default-dashboard-data-did-change"
|
||||
};
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.EventBreakpoint = class EventBreakpoint extends WI.Breakpoint
|
||||
{
|
||||
constructor(type, {eventName, caseSensitive, isRegex, eventListener, disabled, actions, condition, ignoreCount, autoContinue} = {})
|
||||
{
|
||||
// COMPATIBILITY (iOS 13): DOMDebugger.EventBreakpointTypes.Timer was removed.
|
||||
if (type === "timer") {
|
||||
switch (eventName) {
|
||||
case "setInterval":
|
||||
type = WI.EventBreakpoint.Type.Interval;
|
||||
break;
|
||||
|
||||
case "setTimeout":
|
||||
type = WI.EventBreakpoint.Type.Timeout;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.assert(Object.values(WI.EventBreakpoint.Type).includes(type), type);
|
||||
console.assert(!eventName || type === WI.EventBreakpoint.Type.Listener, eventName);
|
||||
console.assert(caseSensitive === undefined || (type === WI.EventBreakpoint.Type.Listener && eventName), caseSensitive);
|
||||
console.assert(isRegex === undefined || (type === WI.EventBreakpoint.Type.Listener && eventName), isRegex);
|
||||
console.assert(!eventListener || type === WI.EventBreakpoint.Type.Listener, eventListener);
|
||||
|
||||
super({disabled, condition, actions, ignoreCount, autoContinue});
|
||||
|
||||
this._type = type;
|
||||
this._eventName = eventName || null;
|
||||
this._caseSensitive = caseSensitive !== undefined ? !!caseSensitive : true;
|
||||
this._isRegex = isRegex !== undefined ? !!isRegex : false;
|
||||
this._eventListener = eventListener || null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static get supportsEditing()
|
||||
{
|
||||
// COMPATIBILITY (iOS 14): DOMDebugger.setEventBreakpoint did not have an "options" parameter yet.
|
||||
return InspectorBackend.hasCommand("DOMDebugger.setEventBreakpoint", "options");
|
||||
}
|
||||
|
||||
static get supportsCaseSensitive()
|
||||
{
|
||||
// COMPATIBILITY (macOS 13.0, iOS 16.0): DOMDebugger.setEventBreakpoint did not have a "caseSensitive" parameter yet.
|
||||
return InspectorBackend.hasCommand("DOMDebugger.setEventBreakpoint", "caseSensitive");
|
||||
}
|
||||
|
||||
static get supportsIsRegex()
|
||||
{
|
||||
// COMPATIBILITY (macOS 13.0, iOS 16.0): DOMDebugger.setEventBreakpoint did not have a "isRegex" parameter yet.
|
||||
return InspectorBackend.hasCommand("DOMDebugger.setEventBreakpoint", "isRegex");
|
||||
}
|
||||
|
||||
static fromJSON(json)
|
||||
{
|
||||
return new WI.EventBreakpoint(json.type, {
|
||||
eventName: json.eventName,
|
||||
caseSensitive: json.caseSensitive,
|
||||
isRegex: json.isRegex,
|
||||
disabled: json.disabled,
|
||||
condition: json.condition,
|
||||
actions: json.actions?.map((actionJSON) => WI.BreakpointAction.fromJSON(actionJSON)) || [],
|
||||
ignoreCount: json.ignoreCount,
|
||||
autoContinue: json.autoContinue,
|
||||
});
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get type() { return this._type; }
|
||||
get eventName() { return this._eventName; }
|
||||
get caseSensitive() { return this._caseSensitive; }
|
||||
get isRegex() { return this._isRegex; }
|
||||
get eventListener() { return this._eventListener; }
|
||||
|
||||
get displayName()
|
||||
{
|
||||
switch (this) {
|
||||
case WI.domDebuggerManager.allAnimationFramesBreakpoint:
|
||||
return WI.repeatedUIString.allAnimationFrames();
|
||||
|
||||
case WI.domDebuggerManager.allIntervalsBreakpoint:
|
||||
return WI.repeatedUIString.allIntervals();
|
||||
|
||||
case WI.domDebuggerManager.allListenersBreakpoint:
|
||||
return WI.repeatedUIString.allEvents();
|
||||
|
||||
case WI.domDebuggerManager.allTimeoutsBreakpoint:
|
||||
return WI.repeatedUIString.allTimeouts();
|
||||
}
|
||||
|
||||
console.assert(this._type === WI.EventBreakpoint.Type.Listener && this._eventName, this);
|
||||
|
||||
if (this._isRegex)
|
||||
return "/" + this._eventName + "/" + (!this._caseSensitive ? "i" : "");
|
||||
|
||||
let displayName = this._eventName;
|
||||
if (!this._caseSensitive)
|
||||
displayName = WI.UIString("%s (Case Insensitive)", "%s (Case Insensitive) @ EventBreakpoint", "Label for case-insensitive match pattern of an event breakpoint.").format(displayName);
|
||||
return displayName;
|
||||
}
|
||||
|
||||
get special()
|
||||
{
|
||||
switch (this) {
|
||||
case WI.domDebuggerManager.allAnimationFramesBreakpoint:
|
||||
case WI.domDebuggerManager.allIntervalsBreakpoint:
|
||||
case WI.domDebuggerManager.allListenersBreakpoint:
|
||||
case WI.domDebuggerManager.allTimeoutsBreakpoint:
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.special;
|
||||
}
|
||||
|
||||
get editable()
|
||||
{
|
||||
if (this._eventListener) {
|
||||
// COMPATIBILITY (iOS 14): DOM.setBreakpointForEventListener did not have an "options" parameter yet.
|
||||
return InspectorBackend.hasCommand("DOM.setBreakpointForEventListener", "options");
|
||||
}
|
||||
|
||||
return WI.EventBreakpoint.supportsEditing || super.editable;
|
||||
}
|
||||
|
||||
matches(eventName)
|
||||
{
|
||||
if (!eventName || this.disabled)
|
||||
return false;
|
||||
|
||||
if (this._isRegex)
|
||||
return (new RegExp(this._eventName, !this._caseSensitive ? "i" : "")).test(eventName);
|
||||
|
||||
if (!this._caseSensitive)
|
||||
return eventName.toLowerCase() === this._eventName.toLowerCase();
|
||||
|
||||
return eventName === this._eventName;
|
||||
}
|
||||
|
||||
equals(other)
|
||||
{
|
||||
console.assert(other instanceof WI.EventBreakpoint, other);
|
||||
|
||||
return this._eventName === other.eventName
|
||||
&& this._caseSensitive === other.caseSensitive
|
||||
&& this._isRegex === other.isRegex;
|
||||
}
|
||||
|
||||
remove()
|
||||
{
|
||||
super.remove();
|
||||
|
||||
if (this._eventListener)
|
||||
WI.domManager.removeBreakpointForEventListener(this._eventListener);
|
||||
else
|
||||
WI.domDebuggerManager.removeEventBreakpoint(this);
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie["event-breakpoint-type"] = this._type;
|
||||
if (this._eventName) {
|
||||
cookie["event-breakpoint-event-name"] = this._eventName;
|
||||
cookie["event-breakpoint-case-sensitive"] = this._caseSensitive;
|
||||
cookie["event-breakpoint-is-regex"] = this._isRegex;
|
||||
}
|
||||
if (this._eventListener)
|
||||
cookie["event-breakpoint-event-listener"] = this._eventListener.eventListenerId;
|
||||
}
|
||||
|
||||
toJSON(key)
|
||||
{
|
||||
let json = super.toJSON(key);
|
||||
json.type = this._type;
|
||||
if (this._eventName) {
|
||||
json.eventName = this._eventName;
|
||||
json.caseSensitive = this._caseSensitive;
|
||||
json.isRegex = this._isRegex;
|
||||
}
|
||||
if (key === WI.ObjectStore.toJSONSymbol)
|
||||
json[WI.objectStores.eventBreakpoints.keyPath] = this._type + (this._eventName ? ":" + this._eventName + "-" + this._caseSensitive + "-" + this._isRegex : "");
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
WI.EventBreakpoint.Type = {
|
||||
AnimationFrame: "animation-frame",
|
||||
Interval: "interval",
|
||||
Listener: "listener",
|
||||
Timeout: "timeout",
|
||||
};
|
||||
|
||||
WI.EventBreakpoint.ReferencePage = WI.ReferencePage.EventBreakpoints;
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.ExecutionContext = class ExecutionContext
|
||||
{
|
||||
constructor(target, id, type, name, frame)
|
||||
{
|
||||
console.assert(target instanceof WI.Target);
|
||||
console.assert(typeof id === "number" || id === WI.RuntimeManager.TopLevelExecutionContextIdentifier);
|
||||
console.assert(Object.values(WI.ExecutionContext.Type).includes(type));
|
||||
console.assert(!name || typeof name === "string");
|
||||
console.assert(frame instanceof WI.Frame || id === WI.RuntimeManager.TopLevelExecutionContextIdentifier);
|
||||
|
||||
this._target = target;
|
||||
this._id = id;
|
||||
this._type = type || WI.ExecutionContext.Type.Internal;
|
||||
this._name = name || "";
|
||||
this._frame = frame || null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static typeFromPayload(payload)
|
||||
{
|
||||
// COMPATIBILITY (iOS 13.1): `Runtime.ExecutionContextType` did not exist yet.
|
||||
if (!("type" in payload))
|
||||
return payload.isPageContext ? WI.ExecutionContext.Type.Normal : WI.ExecutionContext.Type.Internal;
|
||||
|
||||
switch (payload.type) {
|
||||
case InspectorBackend.Enum.Runtime.ExecutionContextType.Normal:
|
||||
return WI.ExecutionContext.Type.Normal;
|
||||
case InspectorBackend.Enum.Runtime.ExecutionContextType.User:
|
||||
return WI.ExecutionContext.Type.User;
|
||||
case InspectorBackend.Enum.Runtime.ExecutionContextType.Internal:
|
||||
return WI.ExecutionContext.Type.Internal;
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown Runtime.ExecutionContextType", payload.type);
|
||||
return WI.ExecutionContext.Type.Internal;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get target() { return this._target; }
|
||||
get id() { return this._id; }
|
||||
get type() { return this._type; }
|
||||
get name() { return this._name; }
|
||||
get frame() { return this._frame; }
|
||||
};
|
||||
|
||||
WI.ExecutionContext.Type = {
|
||||
Normal: "normal",
|
||||
User: "user",
|
||||
Internal: "internal",
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.ExecutionContextList = class ExecutionContextList
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this._contexts = [];
|
||||
this._pageExecutionContext = null;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get pageExecutionContext()
|
||||
{
|
||||
return this._pageExecutionContext;
|
||||
}
|
||||
|
||||
get contexts()
|
||||
{
|
||||
return this._contexts;
|
||||
}
|
||||
|
||||
add(context)
|
||||
{
|
||||
// COMPATIBILITY (iOS 13.0): Older iOS releases will send duplicates.
|
||||
// Newer releases will not and this check should be removed eventually.
|
||||
if (context.type === WI.ExecutionContext.Type.Normal && this._pageExecutionContext) {
|
||||
console.assert(context.id === this._pageExecutionContext.id);
|
||||
return;
|
||||
}
|
||||
|
||||
this._contexts.push(context);
|
||||
|
||||
if (context.type === WI.ExecutionContext.Type.Normal && context.target.type === WI.TargetType.Page) {
|
||||
console.assert(!this._pageExecutionContext);
|
||||
this._pageExecutionContext = context;
|
||||
}
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
this._contexts = [];
|
||||
this._pageExecutionContext = null;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.FPSInstrument = class FPSInstrument extends WI.Instrument
|
||||
{
|
||||
// Protected
|
||||
|
||||
get timelineRecordType()
|
||||
{
|
||||
return WI.TimelineRecord.Type.RenderingFrame;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2020-2022 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Font = class Font
|
||||
{
|
||||
constructor(name, variationAxes, {synthesizedBold, synthesizedOblique} = {})
|
||||
{
|
||||
this._name = name;
|
||||
this._variationAxes = variationAxes;
|
||||
|
||||
// COMPATIBILITY (macOS 13.0, iOS 16.0): CSS.Font.synthesizedBold and CSS.Font.synthesizedOblique did not exist yet.
|
||||
this._synthesizedBold = !!synthesizedBold;
|
||||
this._synthesizedOblique = !!synthesizedOblique;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromPayload(payload)
|
||||
{
|
||||
let variationAxes = payload.variationAxes.map((axisPayload) => WI.FontVariationAxis.fromPayload(axisPayload));
|
||||
|
||||
let synthesizedBold = payload.synthesizedBold;
|
||||
let synthesizedOblique = payload.synthesizedOblique;
|
||||
|
||||
return new WI.Font(payload.displayName, variationAxes, {synthesizedBold, synthesizedOblique});
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get name() { return this._name; }
|
||||
get variationAxes() { return this._variationAxes; }
|
||||
get synthesizedBold() { return this._synthesizedBold; }
|
||||
get synthesizedOblique() { return this._synthesizedOblique; }
|
||||
|
||||
variationAxis(tag)
|
||||
{
|
||||
return this._variationAxes.find((axis) => axis.tag === tag);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.FontStyles = class FontStyles
|
||||
{
|
||||
constructor(nodeStyles)
|
||||
{
|
||||
this._nodeStyles = nodeStyles;
|
||||
|
||||
this._featuresMap = new Map;
|
||||
this._variationsMap = new Map;
|
||||
this._propertiesMap = new Map;
|
||||
|
||||
this._authoredFontVariationSettingsMap = new Map;
|
||||
this._effectiveWritablePropertyForNameMap = new Map;
|
||||
|
||||
// A change in the number of axes or their tags is considered a significant change.
|
||||
// A change to the value of a known axis is not considered a significant change.
|
||||
this._significantChangeSinceLastRefresh = true;
|
||||
|
||||
this._variationAxesTags = [];
|
||||
this._registeredAxesTags = [];
|
||||
|
||||
const forceSignificantChange = true;
|
||||
this.refresh(forceSignificantChange);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get featuresMap() { return this._featuresMap; }
|
||||
|
||||
get variationsMap() { return this._variationsMap; }
|
||||
|
||||
get propertiesMap() { return this._propertiesMap; }
|
||||
|
||||
get significantChangeSinceLastRefresh() { return this._significantChangeSinceLastRefresh; }
|
||||
|
||||
// Static
|
||||
|
||||
static fontPropertyForAxisTag(tag) {
|
||||
const tagToPropertyMap = {
|
||||
"wght": "font-weight",
|
||||
"wdth": "font-stretch",
|
||||
"slnt": "font-style",
|
||||
"ital": "font-style",
|
||||
}
|
||||
|
||||
return tagToPropertyMap[tag];
|
||||
}
|
||||
|
||||
static axisValueToFontPropertyValue(tag, value)
|
||||
{
|
||||
switch (tag) {
|
||||
case "wdth":
|
||||
return `${value}%`;
|
||||
case "slnt":
|
||||
return `oblique ${value}deg`;
|
||||
case "ital":
|
||||
return value >= 1 ? "italic" : "normal";
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
static fontPropertyValueToAxisValue(tag, value)
|
||||
{
|
||||
switch (tag) {
|
||||
case "wdth":
|
||||
return parseFloat(value);
|
||||
case "ital":
|
||||
case "slnt":
|
||||
// See: https://w3c.github.io/csswg-drafts/css-fonts/#valdef-font-style-oblique-angle--90deg-90deg
|
||||
const obliqueAngleDefaultValue = 14;
|
||||
|
||||
if (value === "normal")
|
||||
return 0;
|
||||
|
||||
if (tag === "ital" && (value === "oblique" || value === "italic"))
|
||||
return 1;
|
||||
|
||||
if (tag === "slnt" && (value === "oblique" || value === "italic"))
|
||||
return obliqueAngleDefaultValue;
|
||||
|
||||
let degrees = value.match(/oblique (?<degrees>-?\d+(\.\d+)?)deg/)?.groups?.degrees;
|
||||
if (degrees && tag === "ital")
|
||||
return parseFloat(degrees) >= obliqueAngleDefaultValue ? 1 : 0; // The `ital` variation axis acts as an on/off toggle (0 = off, 1 = on).
|
||||
|
||||
if (degrees && tag === "slnt")
|
||||
return parseFloat(degrees);
|
||||
|
||||
console.assert(false, `Unexpected font property value associated with variation axis ${tag}`, value);
|
||||
break;
|
||||
default:
|
||||
return parseFloat(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
writeFontVariation(tag, value)
|
||||
{
|
||||
let targetPropertyName = WI.FontStyles.fontPropertyForAxisTag(tag);
|
||||
let targetPropertyValue;
|
||||
if (targetPropertyName && !this._authoredFontVariationSettingsMap.has(tag))
|
||||
targetPropertyValue = WI.FontStyles.axisValueToFontPropertyValue(tag, value);
|
||||
else {
|
||||
this._authoredFontVariationSettingsMap.set(tag, value);
|
||||
let axes = [];
|
||||
for (let [tag, value] of this._authoredFontVariationSettingsMap) {
|
||||
axes.push(`"${tag}" ${value}`);
|
||||
}
|
||||
|
||||
targetPropertyName = "font-variation-settings";
|
||||
targetPropertyValue = axes.join(", ");
|
||||
}
|
||||
|
||||
const createIfMissing = true;
|
||||
let cssProperty = this._effectiveWritablePropertyForName(targetPropertyName, createIfMissing);
|
||||
cssProperty.rawValue = targetPropertyValue;
|
||||
}
|
||||
|
||||
refresh(forceSignificantChange)
|
||||
{
|
||||
this._effectiveWritablePropertyForNameMap.clear();
|
||||
|
||||
let prevVariationAxisTags = this._variationAxesTags.slice();
|
||||
let prevRegisteredAxisTags = this._registeredAxesTags.slice();
|
||||
this._variationAxesTags = [];
|
||||
this._registeredAxesTags = [];
|
||||
|
||||
this._calculateFontProperties();
|
||||
|
||||
if (forceSignificantChange)
|
||||
this._significantChangeSinceLastRefresh = true;
|
||||
else
|
||||
this._significantChangeSinceLastRefresh = !Array.shallowEqual(prevRegisteredAxisTags, this._registeredAxesTags) || !Array.shallowEqual(prevVariationAxisTags, this._variationAxesTags);
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_calculateFontProperties()
|
||||
{
|
||||
this._featuresMap = this._calculateFontFeatureAxes(this._nodeStyles);
|
||||
this._variationsMap = this._calculateFontVariationAxes(this._nodeStyles);
|
||||
this._propertiesMap = this._calculateProperties({domNodeStyle: this._nodeStyles, featuresMap: this._featuresMap, variationsMap: this._variationsMap});
|
||||
}
|
||||
|
||||
_calculateProperties(style)
|
||||
{
|
||||
let resultProperties = new Map;
|
||||
|
||||
this._populateProperty("font-size", style, resultProperties, {
|
||||
keywordComputedReplacements: ["larger", "smaller", "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "xxx-large"],
|
||||
});
|
||||
this._populateProperty("font-style", style, resultProperties, {
|
||||
variations: ["ital", "slnt"],
|
||||
keywordReplacements: new Map([
|
||||
["oblique", "oblique 14deg"],
|
||||
]),
|
||||
});
|
||||
this._populateProperty("font-weight", style, resultProperties, {
|
||||
variations: ["wght"],
|
||||
keywordComputedReplacements: ["bolder", "lighter"],
|
||||
keywordReplacements: new Map([
|
||||
["normal", "400"],
|
||||
["bold", "700"],
|
||||
]),
|
||||
});
|
||||
this._populateProperty("font-stretch", style, resultProperties, {
|
||||
variations: ["wdth"],
|
||||
keywordReplacements: new Map([
|
||||
["ultra-condensed", "50%"],
|
||||
["extra-condensed", "62.5%"],
|
||||
["condensed", "75%"],
|
||||
["semi-condensed", "87.5%"],
|
||||
["normal", "100%"],
|
||||
["semi-expanded", "112.5%"],
|
||||
["expanded", "125%"],
|
||||
["extra-expanded", "150%"],
|
||||
["ultra-expanded", "200%"],
|
||||
]),
|
||||
});
|
||||
|
||||
this._populateProperty("font-variant-ligatures", style, resultProperties, {features: ["liga", "clig", "dlig", "hlig", "calt"]});
|
||||
this._populateProperty("font-variant-position", style, resultProperties, {features: ["subs", "sups"]});
|
||||
this._populateProperty("font-variant-caps", style, resultProperties, {features: ["smcp", "c2sc", "pcap", "c2pc", "unic", "titl"]});
|
||||
this._populateProperty("font-variant-numeric", style, resultProperties, {features: ["lnum", "onum", "pnum", "tnum", "frac", "afrc", "ordn", "zero"]});
|
||||
this._populateProperty("font-variant-alternates", style, resultProperties, {features: ["hist"] });
|
||||
this._populateProperty("font-variant-east-asian", style, resultProperties, {features: ["jp78", "jp83", "jp90", "jp04", "smpl", "trad", "fwid", "pwid", "ruby"]});
|
||||
|
||||
return resultProperties;
|
||||
}
|
||||
|
||||
_calculateFontFeatureAxes(domNodeStyle)
|
||||
{
|
||||
return this._parseFontFeatureOrVariationSettings(domNodeStyle, "font-feature-settings");
|
||||
}
|
||||
|
||||
_calculateFontVariationAxes(domNodeStyle)
|
||||
{
|
||||
this._authoredFontVariationSettingsMap = this._parseFontFeatureOrVariationSettings(domNodeStyle, "font-variation-settings");
|
||||
let resultAxes = new Map;
|
||||
|
||||
if (!this._nodeStyles.computedPrimaryFont)
|
||||
return resultAxes;
|
||||
|
||||
for (let axis of this._nodeStyles.computedPrimaryFont.variationAxes) {
|
||||
// `value` can be undefined.
|
||||
resultAxes.set(axis.tag, {
|
||||
tag: axis.tag,
|
||||
name: axis.name,
|
||||
minimumValue: axis.minimumValue,
|
||||
maximumValue: axis.maximumValue,
|
||||
defaultValue: axis.defaultValue,
|
||||
value: this._authoredFontVariationSettingsMap.get(axis.tag),
|
||||
});
|
||||
|
||||
this._variationAxesTags.push(axis.tag);
|
||||
}
|
||||
|
||||
return resultAxes;
|
||||
}
|
||||
|
||||
_parseFontFeatureOrVariationSettings(domNodeStyle, property)
|
||||
{
|
||||
let cssSettings = new Map;
|
||||
let cssSettingsRawValue = this._computedPropertyValueForName(domNodeStyle, property);
|
||||
|
||||
if (cssSettingsRawValue !== "normal") {
|
||||
for (let axis of cssSettingsRawValue.split(",")) {
|
||||
// Tags can contains upper and lowercase latin letters, numbers, and spaces (only ending with space(s)). Values will be numbers, `on`, or `off`.
|
||||
let [tag, value] = axis.match(WI.FontStyles.SettingPattern);
|
||||
tag = tag.replaceAll(/["']/g, "");
|
||||
if (!value || value === "on")
|
||||
value = 1;
|
||||
else if (value === "off")
|
||||
value = 0;
|
||||
cssSettings.set(tag, parseFloat(value));
|
||||
}
|
||||
}
|
||||
|
||||
return cssSettings;
|
||||
}
|
||||
|
||||
_populateProperty(name, style, resultProperties, {variations, features, keywordComputedReplacements, keywordReplacements})
|
||||
{
|
||||
resultProperties.set(name, this._computeProperty(name, style, {variations, features, keywordComputedReplacements, keywordReplacements}));
|
||||
}
|
||||
|
||||
_computeProperty(name, style, {variations, features, keywordComputedReplacements, keywordReplacements})
|
||||
{
|
||||
variations ??= [];
|
||||
features ??= [];
|
||||
keywordComputedReplacements ??= [];
|
||||
keywordReplacements ??= new Map;
|
||||
|
||||
let resultProperties = {};
|
||||
|
||||
let value = this._effectivePropertyValueForName(style.domNodeStyle, name);
|
||||
|
||||
if (!value || value === "inherit" || keywordComputedReplacements.includes(value))
|
||||
value = this._computedPropertyValueForName(style.domNodeStyle, name);
|
||||
|
||||
if (keywordReplacements.has(value))
|
||||
value = keywordReplacements.get(value);
|
||||
|
||||
resultProperties.value = value;
|
||||
|
||||
for (let fontVariationTag of variations) {
|
||||
let fontVariationAxis = style.variationsMap.get(fontVariationTag);
|
||||
if (fontVariationAxis) {
|
||||
resultProperties.variations ??= new Map;
|
||||
resultProperties.variations.set(fontVariationTag, fontVariationAxis);
|
||||
|
||||
// Remove the tag so it is not presented twice.
|
||||
style.variationsMap.delete(fontVariationTag);
|
||||
|
||||
this._registeredAxesTags.push(fontVariationTag);
|
||||
}
|
||||
}
|
||||
|
||||
for (let fontFeatureSetting of features) {
|
||||
let featureSettingValue = style.featuresMap.get(fontFeatureSetting);
|
||||
if (featureSettingValue || featureSettingValue === 0) {
|
||||
resultProperties.features ??= new Map;
|
||||
resultProperties.features.set(fontFeatureSetting, featureSettingValue);
|
||||
|
||||
// Remove the tag so it is not presented twice.
|
||||
style.featuresMap.delete(fontFeatureSetting);
|
||||
}
|
||||
}
|
||||
|
||||
return resultProperties;
|
||||
}
|
||||
|
||||
_effectivePropertyValueForName(domNodeStyle, name)
|
||||
{
|
||||
return domNodeStyle.effectivePropertyForName(name)?.value || "";
|
||||
}
|
||||
|
||||
_effectiveWritablePropertyForName(name, createIfMissing)
|
||||
{
|
||||
let cssProperty = this._effectiveWritablePropertyForNameMap.get(name);
|
||||
if (cssProperty)
|
||||
return cssProperty;
|
||||
|
||||
// FIXME: <webkit.org/b/250127> Value for edited variation axis should be written to ideal CSS rule in cascade
|
||||
let inlineCSSStyleDeclaration = this._nodeStyles.inlineStyle;
|
||||
let properties = inlineCSSStyleDeclaration.visibleProperties;
|
||||
|
||||
cssProperty = properties.find(property => property.name === name);
|
||||
if (!cssProperty && createIfMissing) {
|
||||
cssProperty = inlineCSSStyleDeclaration.newBlankProperty(properties.length);
|
||||
cssProperty.name = name;
|
||||
}
|
||||
|
||||
if (cssProperty)
|
||||
this._effectiveWritablePropertyForNameMap.set(name, cssProperty);
|
||||
|
||||
return cssProperty;
|
||||
}
|
||||
|
||||
_computedPropertyValueForName(domNodeStyle, name)
|
||||
{
|
||||
return domNodeStyle.computedStyle?.propertyForName(name)?.value || "";
|
||||
}
|
||||
};
|
||||
|
||||
WI.FontStyles.SettingPattern = /[^\s"']+|["']([^"']*)["']/g;
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.FontVariationAxis = class FontVariationAxis
|
||||
{
|
||||
constructor(name, tag, minimumValue, maximumValue, defaultValue)
|
||||
{
|
||||
console.assert(typeof tag === "string" && tag.length === 4, "Invalid font variation axis tag", tag);
|
||||
|
||||
this._name = name;
|
||||
this._tag = tag;
|
||||
this._minimumValue = minimumValue;
|
||||
this._maximumValue = maximumValue;
|
||||
this._defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromPayload(payload)
|
||||
{
|
||||
return new WI.FontVariationAxis(payload.name, payload.tag, payload.minimumValue, payload.maximumValue, payload.defaultValue);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get name() { return this._name; }
|
||||
get tag() { return this._tag; }
|
||||
get minimumValue() { return this._minimumValue; }
|
||||
get maximumValue() { return this._maximumValue; }
|
||||
get defaultValue() { return this._defaultValue; }
|
||||
};
|
||||
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Frame = class Frame extends WI.Object
|
||||
{
|
||||
constructor(id, name, securityOrigin, loaderIdentifier, mainResource)
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(id);
|
||||
|
||||
this._id = id;
|
||||
|
||||
this._name = null;
|
||||
this._securityOrigin = null;
|
||||
|
||||
this._resourceCollection = new WI.ResourceCollection;
|
||||
this._provisionalResourceCollection = new WI.ResourceCollection;
|
||||
this._extraScriptCollection = new WI.ScriptCollection;
|
||||
|
||||
this._childFrameCollection = new WI.FrameCollection;
|
||||
this._childFrameIdentifierMap = new Map;
|
||||
|
||||
this._parentFrame = null;
|
||||
this._isMainFrame = false;
|
||||
|
||||
this._domContentReadyEventTimestamp = NaN;
|
||||
this._loadEventTimestamp = NaN;
|
||||
|
||||
this._executionContextList = new WI.ExecutionContextList;
|
||||
|
||||
this.initialize(name, securityOrigin, loaderIdentifier, mainResource);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get resourceCollection() { return this._resourceCollection; }
|
||||
get extraScriptCollection() { return this._extraScriptCollection; }
|
||||
get childFrameCollection() { return this._childFrameCollection; }
|
||||
|
||||
initialize(name, securityOrigin, loaderIdentifier, mainResource)
|
||||
{
|
||||
console.assert(loaderIdentifier);
|
||||
console.assert(mainResource);
|
||||
|
||||
var oldName = this._name;
|
||||
var oldSecurityOrigin = this._securityOrigin;
|
||||
var oldMainResource = this._mainResource;
|
||||
|
||||
this._name = name || null;
|
||||
this._securityOrigin = securityOrigin || null;
|
||||
this._loaderIdentifier = loaderIdentifier || null;
|
||||
|
||||
this._mainResource = mainResource;
|
||||
this._mainResource._parentFrame = this;
|
||||
|
||||
if (oldMainResource && this._mainResource !== oldMainResource)
|
||||
this._disassociateWithResource(oldMainResource);
|
||||
|
||||
this.removeAllResources();
|
||||
this.removeAllChildFrames();
|
||||
this.clearExecutionContexts();
|
||||
this.clearProvisionalLoad();
|
||||
|
||||
if (this._mainResource !== oldMainResource)
|
||||
this._dispatchMainResourceDidChangeEvent(oldMainResource);
|
||||
|
||||
if (this._securityOrigin !== oldSecurityOrigin)
|
||||
this.dispatchEventToListeners(WI.Frame.Event.SecurityOriginDidChange, {oldSecurityOrigin});
|
||||
|
||||
if (this._name !== oldName)
|
||||
this.dispatchEventToListeners(WI.Frame.Event.NameDidChange, {oldName});
|
||||
}
|
||||
|
||||
startProvisionalLoad(provisionalMainResource)
|
||||
{
|
||||
console.assert(provisionalMainResource);
|
||||
|
||||
this._provisionalMainResource = provisionalMainResource;
|
||||
this._provisionalMainResource._parentFrame = this;
|
||||
|
||||
this._provisionalLoaderIdentifier = provisionalMainResource.loaderIdentifier;
|
||||
|
||||
this._provisionalResourceCollection.clear();
|
||||
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ProvisionalLoadStarted);
|
||||
}
|
||||
|
||||
commitProvisionalLoad(securityOrigin)
|
||||
{
|
||||
console.assert(this._provisionalMainResource);
|
||||
console.assert(this._provisionalLoaderIdentifier);
|
||||
if (!this._provisionalLoaderIdentifier)
|
||||
return;
|
||||
|
||||
var oldSecurityOrigin = this._securityOrigin;
|
||||
var oldMainResource = this._mainResource;
|
||||
|
||||
this._securityOrigin = securityOrigin || null;
|
||||
this._loaderIdentifier = this._provisionalLoaderIdentifier;
|
||||
this._mainResource = this._provisionalMainResource;
|
||||
|
||||
this._domContentReadyEventTimestamp = NaN;
|
||||
this._loadEventTimestamp = NaN;
|
||||
|
||||
if (oldMainResource && this._mainResource !== oldMainResource)
|
||||
this._disassociateWithResource(oldMainResource);
|
||||
|
||||
this.removeAllResources();
|
||||
|
||||
this._resourceCollection = this._provisionalResourceCollection;
|
||||
this._provisionalResourceCollection = new WI.ResourceCollection;
|
||||
this._extraScriptCollection.clear();
|
||||
|
||||
this.clearExecutionContexts(true);
|
||||
this.clearProvisionalLoad(true);
|
||||
this.removeAllChildFrames();
|
||||
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ProvisionalLoadCommitted);
|
||||
|
||||
if (this._mainResource !== oldMainResource)
|
||||
this._dispatchMainResourceDidChangeEvent(oldMainResource);
|
||||
|
||||
if (this._securityOrigin !== oldSecurityOrigin)
|
||||
this.dispatchEventToListeners(WI.Frame.Event.SecurityOriginDidChange, {oldSecurityOrigin});
|
||||
}
|
||||
|
||||
clearProvisionalLoad(skipProvisionalLoadClearedEvent)
|
||||
{
|
||||
if (!this._provisionalLoaderIdentifier)
|
||||
return;
|
||||
|
||||
this._provisionalLoaderIdentifier = null;
|
||||
this._provisionalMainResource = null;
|
||||
this._provisionalResourceCollection.clear();
|
||||
|
||||
if (!skipProvisionalLoadClearedEvent)
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ProvisionalLoadCleared);
|
||||
}
|
||||
|
||||
get id()
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get loaderIdentifier()
|
||||
{
|
||||
return this._loaderIdentifier;
|
||||
}
|
||||
|
||||
get provisionalLoaderIdentifier()
|
||||
{
|
||||
return this._provisionalLoaderIdentifier;
|
||||
}
|
||||
|
||||
get name()
|
||||
{
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get securityOrigin()
|
||||
{
|
||||
return this._securityOrigin;
|
||||
}
|
||||
|
||||
get url()
|
||||
{
|
||||
return this._mainResource.url;
|
||||
}
|
||||
|
||||
get urlComponents()
|
||||
{
|
||||
return this._mainResource.urlComponents;
|
||||
}
|
||||
|
||||
get domTree()
|
||||
{
|
||||
if (!this._domTree)
|
||||
this._domTree = new WI.DOMTree(this);
|
||||
return this._domTree;
|
||||
}
|
||||
|
||||
get pageExecutionContext()
|
||||
{
|
||||
return this._executionContextList.pageExecutionContext;
|
||||
}
|
||||
|
||||
get executionContextList()
|
||||
{
|
||||
return this._executionContextList;
|
||||
}
|
||||
|
||||
clearExecutionContexts(committingProvisionalLoad)
|
||||
{
|
||||
if (this._executionContextList.contexts.length) {
|
||||
let contexts = this._executionContextList.contexts.slice();
|
||||
this._executionContextList.clear();
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ExecutionContextsCleared, {committingProvisionalLoad: !!committingProvisionalLoad, contexts});
|
||||
}
|
||||
}
|
||||
|
||||
addExecutionContext(context)
|
||||
{
|
||||
this._executionContextList.add(context);
|
||||
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ExecutionContextAdded, {context});
|
||||
|
||||
if (this._executionContextList.pageExecutionContext === context)
|
||||
this.dispatchEventToListeners(WI.Frame.Event.PageExecutionContextChanged);
|
||||
}
|
||||
|
||||
get mainResource()
|
||||
{
|
||||
return this._mainResource;
|
||||
}
|
||||
|
||||
get provisionalMainResource()
|
||||
{
|
||||
return this._provisionalMainResource;
|
||||
}
|
||||
|
||||
get parentFrame()
|
||||
{
|
||||
return this._parentFrame;
|
||||
}
|
||||
|
||||
get domContentReadyEventTimestamp()
|
||||
{
|
||||
return this._domContentReadyEventTimestamp;
|
||||
}
|
||||
|
||||
get loadEventTimestamp()
|
||||
{
|
||||
return this._loadEventTimestamp;
|
||||
}
|
||||
|
||||
isMainFrame()
|
||||
{
|
||||
return this._isMainFrame;
|
||||
}
|
||||
|
||||
markAsMainFrame()
|
||||
{
|
||||
this._isMainFrame = true;
|
||||
}
|
||||
|
||||
unmarkAsMainFrame()
|
||||
{
|
||||
this._isMainFrame = false;
|
||||
}
|
||||
|
||||
markDOMContentReadyEvent(timestamp)
|
||||
{
|
||||
console.assert(isNaN(this._domContentReadyEventTimestamp));
|
||||
|
||||
this._domContentReadyEventTimestamp = timestamp || NaN;
|
||||
}
|
||||
|
||||
markLoadEvent(timestamp)
|
||||
{
|
||||
console.assert(isNaN(this._loadEventTimestamp));
|
||||
|
||||
this._loadEventTimestamp = timestamp || NaN;
|
||||
}
|
||||
|
||||
isDetached()
|
||||
{
|
||||
var frame = this;
|
||||
while (frame) {
|
||||
if (frame.isMainFrame())
|
||||
return false;
|
||||
frame = frame.parentFrame;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
childFrameForIdentifier(frameId)
|
||||
{
|
||||
return this._childFrameIdentifierMap.get(frameId) || null;
|
||||
}
|
||||
|
||||
addChildFrame(frame)
|
||||
{
|
||||
console.assert(frame instanceof WI.Frame);
|
||||
if (!(frame instanceof WI.Frame))
|
||||
return;
|
||||
|
||||
if (frame._parentFrame === this)
|
||||
return;
|
||||
|
||||
if (frame._parentFrame)
|
||||
frame._parentFrame.removeChildFrame(frame);
|
||||
|
||||
this._childFrameCollection.add(frame);
|
||||
this._childFrameIdentifierMap.set(frame._id, frame);
|
||||
|
||||
frame._parentFrame = this;
|
||||
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ChildFrameWasAdded, {childFrame: frame});
|
||||
}
|
||||
|
||||
removeChildFrame(frameOrFrameId)
|
||||
{
|
||||
console.assert(frameOrFrameId);
|
||||
|
||||
let childFrameId = frameOrFrameId;
|
||||
if (childFrameId instanceof WI.Frame)
|
||||
childFrameId = frameOrFrameId._id;
|
||||
|
||||
// Fetch the frame by id even if we were passed a WI.Frame.
|
||||
// We do this incase the WI.Frame is a new object that isn't
|
||||
// in _childFrameCollection, but the id is a valid child frame.
|
||||
let childFrame = this.childFrameForIdentifier(childFrameId);
|
||||
console.assert(childFrame instanceof WI.Frame);
|
||||
if (!(childFrame instanceof WI.Frame))
|
||||
return;
|
||||
|
||||
console.assert(childFrame.parentFrame === this);
|
||||
|
||||
this._childFrameCollection.remove(childFrame);
|
||||
this._childFrameIdentifierMap.delete(childFrame._id);
|
||||
|
||||
childFrame._detachFromParentFrame();
|
||||
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ChildFrameWasRemoved, {childFrame});
|
||||
}
|
||||
|
||||
removeAllChildFrames()
|
||||
{
|
||||
this._detachFromParentFrame();
|
||||
|
||||
for (let childFrame of this._childFrameCollection)
|
||||
childFrame.removeAllChildFrames();
|
||||
|
||||
this._childFrameCollection.clear();
|
||||
this._childFrameIdentifierMap.clear();
|
||||
|
||||
this.dispatchEventToListeners(WI.Frame.Event.AllChildFramesRemoved);
|
||||
}
|
||||
|
||||
resourcesForURL(url, recursivelySearchChildFrames)
|
||||
{
|
||||
let resources = this._resourceCollection.resourcesForURL(url);
|
||||
|
||||
// Check the main resources of the child frames for the requested URL.
|
||||
for (let childFrame of this._childFrameCollection) {
|
||||
if (childFrame.mainResource.url === url)
|
||||
resources.add(childFrame.mainResource);
|
||||
}
|
||||
|
||||
if (recursivelySearchChildFrames) {
|
||||
for (let childFrame of this._childFrameCollection)
|
||||
resources.addAll(childFrame.resourcesForURL(url, recursivelySearchChildFrames));
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
resourceCollectionForType(type)
|
||||
{
|
||||
return this._resourceCollection.resourceCollectionForType(type);
|
||||
}
|
||||
|
||||
addResource(resource)
|
||||
{
|
||||
console.assert(resource instanceof WI.Resource);
|
||||
if (!(resource instanceof WI.Resource))
|
||||
return;
|
||||
|
||||
if (resource.parentFrame === this)
|
||||
return;
|
||||
|
||||
if (resource.parentFrame)
|
||||
resource.parentFrame.remove(resource);
|
||||
|
||||
this._associateWithResource(resource);
|
||||
|
||||
if (this._isProvisionalResource(resource)) {
|
||||
this._provisionalResourceCollection.add(resource);
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ProvisionalResourceWasAdded, {resource});
|
||||
} else {
|
||||
this._resourceCollection.add(resource);
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ResourceWasAdded, {resource});
|
||||
}
|
||||
}
|
||||
|
||||
removeResource(resource)
|
||||
{
|
||||
// This does not remove provisional resources.
|
||||
|
||||
this._resourceCollection.remove(resource);
|
||||
|
||||
this._disassociateWithResource(resource);
|
||||
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ResourceWasRemoved, {resource});
|
||||
}
|
||||
|
||||
removeAllResources()
|
||||
{
|
||||
// This does not remove provisional resources, use clearProvisionalLoad for that.
|
||||
|
||||
if (!this._resourceCollection.size)
|
||||
return;
|
||||
|
||||
for (let resource of this._resourceCollection)
|
||||
this._disassociateWithResource(resource);
|
||||
|
||||
this._resourceCollection.clear();
|
||||
|
||||
this.dispatchEventToListeners(WI.Frame.Event.AllResourcesRemoved);
|
||||
}
|
||||
|
||||
addExtraScript(script)
|
||||
{
|
||||
this._extraScriptCollection.add(script);
|
||||
|
||||
this.dispatchEventToListeners(WI.Frame.Event.ExtraScriptAdded, {script});
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie[WI.Frame.MainResourceURLCookieKey] = this.mainResource.url.hash;
|
||||
cookie[WI.Frame.IsMainFrameCookieKey] = this._isMainFrame;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_detachFromParentFrame()
|
||||
{
|
||||
if (this._domTree) {
|
||||
this._domTree.disconnect();
|
||||
this._domTree = null;
|
||||
}
|
||||
|
||||
this._parentFrame = null;
|
||||
}
|
||||
|
||||
_isProvisionalResource(resource)
|
||||
{
|
||||
return resource.loaderIdentifier && this._provisionalLoaderIdentifier && resource.loaderIdentifier === this._provisionalLoaderIdentifier;
|
||||
}
|
||||
|
||||
_associateWithResource(resource)
|
||||
{
|
||||
console.assert(!resource._parentFrame);
|
||||
if (resource._parentFrame)
|
||||
return;
|
||||
|
||||
resource._parentFrame = this;
|
||||
}
|
||||
|
||||
_disassociateWithResource(resource)
|
||||
{
|
||||
console.assert(resource.parentFrame === this);
|
||||
if (resource.parentFrame !== this)
|
||||
return;
|
||||
|
||||
resource._parentFrame = null;
|
||||
}
|
||||
|
||||
_dispatchMainResourceDidChangeEvent(oldMainResource)
|
||||
{
|
||||
this.dispatchEventToListeners(WI.Frame.Event.MainResourceDidChange, {oldMainResource});
|
||||
}
|
||||
};
|
||||
|
||||
WI.Frame.Event = {
|
||||
NameDidChange: "frame-name-did-change",
|
||||
SecurityOriginDidChange: "frame-security-origin-did-change",
|
||||
MainResourceDidChange: "frame-main-resource-did-change",
|
||||
ProvisionalLoadStarted: "frame-provisional-load-started",
|
||||
ProvisionalLoadCommitted: "frame-provisional-load-committed",
|
||||
ProvisionalLoadCleared: "frame-provisional-load-cleared",
|
||||
ProvisionalResourceWasAdded: "frame-provisional-resource-was-added",
|
||||
ResourceWasAdded: "frame-resource-was-added",
|
||||
ResourceWasRemoved: "frame-resource-was-removed",
|
||||
AllResourcesRemoved: "frame-all-resources-removed",
|
||||
ExtraScriptAdded: "frame-extra-script-added",
|
||||
ChildFrameWasAdded: "frame-child-frame-was-added",
|
||||
ChildFrameWasRemoved: "frame-child-frame-was-removed",
|
||||
AllChildFramesRemoved: "frame-all-child-frames-removed",
|
||||
PageExecutionContextChanged: "frame-page-execution-context-changed",
|
||||
ExecutionContextAdded: "frame-execution-context-added",
|
||||
ExecutionContextsCleared: "frame-execution-contexts-cleared"
|
||||
};
|
||||
|
||||
WI.Frame.TypeIdentifier = "Frame";
|
||||
WI.Frame.MainResourceURLCookieKey = "frame-main-resource-url";
|
||||
WI.Frame.IsMainFrameCookieKey = "frame-is-main-frame";
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.GarbageCollection = class GarbageCollection
|
||||
{
|
||||
constructor(type, startTime, endTime)
|
||||
{
|
||||
console.assert(endTime >= startTime);
|
||||
|
||||
this._type = type;
|
||||
this._startTime = startTime;
|
||||
this._endTime = endTime;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromPayload(payload)
|
||||
{
|
||||
let type = WI.GarbageCollection.Type.Full;
|
||||
if (payload.type === InspectorBackend.Enum.Heap.GarbageCollectionType.Partial)
|
||||
type = WI.GarbageCollection.Type.Partial;
|
||||
|
||||
return new WI.GarbageCollection(type, payload.startTime, payload.endTime);
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static fromJSON(json)
|
||||
{
|
||||
let {type, startTime, endTime} = json;
|
||||
return new WI.GarbageCollection(type, startTime, endTime);
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return {
|
||||
__type: "GarbageCollection",
|
||||
type: this.type,
|
||||
startTime: this.startTime,
|
||||
endTime: this.endTime,
|
||||
};
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get type() { return this._type; }
|
||||
get startTime() { return this._startTime; }
|
||||
get endTime() { return this._endTime; }
|
||||
|
||||
get duration()
|
||||
{
|
||||
return this._endTime - this._startTime;
|
||||
}
|
||||
};
|
||||
|
||||
WI.GarbageCollection.Type = {
|
||||
Partial: "partial",
|
||||
Full: "full",
|
||||
};
|
||||
@@ -0,0 +1,660 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Point = class Point
|
||||
{
|
||||
constructor(x, y)
|
||||
{
|
||||
this.x = x || 0;
|
||||
this.y = y || 0;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromEvent(event)
|
||||
{
|
||||
return new WI.Point(event.pageX, event.pageY);
|
||||
}
|
||||
|
||||
static fromEventInElement(event, element)
|
||||
{
|
||||
let rect = element.getBoundingClientRect();
|
||||
return new WI.Point(event.pageX - rect.x, event.pageY - rect.y);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
toString()
|
||||
{
|
||||
return "WI.Point[" + this.x + "," + this.y + "]";
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.Point(this.x, this.y);
|
||||
}
|
||||
|
||||
equals(anotherPoint)
|
||||
{
|
||||
return this.x === anotherPoint.x && this.y === anotherPoint.y;
|
||||
}
|
||||
|
||||
distance(anotherPoint)
|
||||
{
|
||||
let dx = anotherPoint.x - this.x;
|
||||
let dy = anotherPoint.y - this.y;
|
||||
return Math.sqrt((dx * dx) + (dy * dy));
|
||||
}
|
||||
};
|
||||
|
||||
WI.Size = class Size
|
||||
{
|
||||
constructor(width, height)
|
||||
{
|
||||
this.width = width || 0;
|
||||
this.height = height || 0;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
toString()
|
||||
{
|
||||
return "WI.Size[" + this.width + "," + this.height + "]";
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.Size(this.width, this.height);
|
||||
}
|
||||
|
||||
equals(anotherSize)
|
||||
{
|
||||
return this.width === anotherSize.width && this.height === anotherSize.height;
|
||||
}
|
||||
};
|
||||
|
||||
WI.Size.ZERO_SIZE = new WI.Size(0, 0);
|
||||
|
||||
|
||||
WI.Rect = class Rect
|
||||
{
|
||||
constructor(x, y, width, height)
|
||||
{
|
||||
this.origin = new WI.Point(x || 0, y || 0);
|
||||
this.size = new WI.Size(width || 0, height || 0);
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static rectFromClientRect(clientRect)
|
||||
{
|
||||
return new WI.Rect(clientRect.left, clientRect.top, clientRect.width, clientRect.height);
|
||||
}
|
||||
|
||||
static unionOfRects(rects)
|
||||
{
|
||||
var union = rects[0];
|
||||
for (var i = 1; i < rects.length; ++i)
|
||||
union = union.unionWithRect(rects[i]);
|
||||
return union;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
toString()
|
||||
{
|
||||
return "WI.Rect[" + [this.origin.x, this.origin.y, this.size.width, this.size.height].join(", ") + "]";
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.Rect(this.origin.x, this.origin.y, this.size.width, this.size.height);
|
||||
}
|
||||
|
||||
equals(anotherRect)
|
||||
{
|
||||
return this.origin.equals(anotherRect.origin) && this.size.equals(anotherRect.size);
|
||||
}
|
||||
|
||||
inset(insets)
|
||||
{
|
||||
return new WI.Rect(
|
||||
this.origin.x + insets.left,
|
||||
this.origin.y + insets.top,
|
||||
this.size.width - insets.left - insets.right,
|
||||
this.size.height - insets.top - insets.bottom
|
||||
);
|
||||
}
|
||||
|
||||
pad(padding)
|
||||
{
|
||||
return new WI.Rect(
|
||||
this.origin.x - padding,
|
||||
this.origin.y - padding,
|
||||
this.size.width + padding * 2,
|
||||
this.size.height + padding * 2
|
||||
);
|
||||
}
|
||||
|
||||
minX()
|
||||
{
|
||||
return this.origin.x;
|
||||
}
|
||||
|
||||
minY()
|
||||
{
|
||||
return this.origin.y;
|
||||
}
|
||||
|
||||
midX()
|
||||
{
|
||||
return this.origin.x + (this.size.width / 2);
|
||||
}
|
||||
|
||||
midY()
|
||||
{
|
||||
return this.origin.y + (this.size.height / 2);
|
||||
}
|
||||
|
||||
maxX()
|
||||
{
|
||||
return this.origin.x + this.size.width;
|
||||
}
|
||||
|
||||
maxY()
|
||||
{
|
||||
return this.origin.y + this.size.height;
|
||||
}
|
||||
|
||||
intersectionWithRect(rect)
|
||||
{
|
||||
var x1 = Math.max(this.minX(), rect.minX());
|
||||
var x2 = Math.min(this.maxX(), rect.maxX());
|
||||
if (x1 > x2)
|
||||
return WI.Rect.ZERO_RECT;
|
||||
var intersection = new WI.Rect;
|
||||
intersection.origin.x = x1;
|
||||
intersection.size.width = x2 - x1;
|
||||
var y1 = Math.max(this.minY(), rect.minY());
|
||||
var y2 = Math.min(this.maxY(), rect.maxY());
|
||||
if (y1 > y2)
|
||||
return WI.Rect.ZERO_RECT;
|
||||
intersection.origin.y = y1;
|
||||
intersection.size.height = y2 - y1;
|
||||
return intersection;
|
||||
}
|
||||
|
||||
unionWithRect(rect)
|
||||
{
|
||||
var x = Math.min(this.minX(), rect.minX());
|
||||
var y = Math.min(this.minY(), rect.minY());
|
||||
var width = Math.max(this.maxX(), rect.maxX()) - x;
|
||||
var height = Math.max(this.maxY(), rect.maxY()) - y;
|
||||
return new WI.Rect(x, y, width, height);
|
||||
}
|
||||
|
||||
round()
|
||||
{
|
||||
return new WI.Rect(
|
||||
Math.floor(this.origin.x),
|
||||
Math.floor(this.origin.y),
|
||||
Math.ceil(this.size.width),
|
||||
Math.ceil(this.size.height)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
WI.Rect.ZERO_RECT = new WI.Rect(0, 0, 0, 0);
|
||||
|
||||
|
||||
WI.EdgeInsets = class EdgeInsets
|
||||
{
|
||||
constructor(top, right, bottom, left)
|
||||
{
|
||||
console.assert(arguments.length === 1 || arguments.length === 4);
|
||||
|
||||
if (arguments.length === 1) {
|
||||
this.top = top;
|
||||
this.right = top;
|
||||
this.bottom = top;
|
||||
this.left = top;
|
||||
} else if (arguments.length === 4) {
|
||||
this.top = top;
|
||||
this.right = right;
|
||||
this.bottom = bottom;
|
||||
this.left = left;
|
||||
}
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
equals(anotherInset)
|
||||
{
|
||||
return this.top === anotherInset.top && this.right === anotherInset.right
|
||||
&& this.bottom === anotherInset.bottom && this.left === anotherInset.left;
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.EdgeInsets(this.top, this.right, this.bottom, this.left);
|
||||
}
|
||||
};
|
||||
|
||||
WI.RectEdge = {
|
||||
MIN_X: 0,
|
||||
MIN_Y: 1,
|
||||
MAX_X: 2,
|
||||
MAX_Y: 3
|
||||
};
|
||||
|
||||
WI.Quad = class Quad
|
||||
{
|
||||
constructor(quad)
|
||||
{
|
||||
this.points = [
|
||||
new WI.Point(quad[0], quad[1]), // top left
|
||||
new WI.Point(quad[2], quad[3]), // top right
|
||||
new WI.Point(quad[4], quad[5]), // bottom right
|
||||
new WI.Point(quad[6], quad[7]) // bottom left
|
||||
];
|
||||
|
||||
this.width = Math.round(Math.sqrt(Math.pow(quad[0] - quad[2], 2) + Math.pow(quad[1] - quad[3], 2)));
|
||||
this.height = Math.round(Math.sqrt(Math.pow(quad[0] - quad[6], 2) + Math.pow(quad[1] - quad[7], 2)));
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static fromJSON(json)
|
||||
{
|
||||
return new WI.Quad(json);
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return this.toProtocol();
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
toProtocol()
|
||||
{
|
||||
return [
|
||||
this.points[0].x, this.points[0].y,
|
||||
this.points[1].x, this.points[1].y,
|
||||
this.points[2].x, this.points[2].y,
|
||||
this.points[3].x, this.points[3].y
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
WI.Polygon = class Polygon
|
||||
{
|
||||
constructor(points)
|
||||
{
|
||||
this.points = points;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
bounds()
|
||||
{
|
||||
var minX = Number.MAX_VALUE;
|
||||
var minY = Number.MAX_VALUE;
|
||||
var maxX = -Number.MAX_VALUE;
|
||||
var maxY = -Number.MAX_VALUE;
|
||||
for (var point of this.points) {
|
||||
minX = Math.min(minX, point.x);
|
||||
maxX = Math.max(maxX, point.x);
|
||||
minY = Math.min(minY, point.y);
|
||||
maxY = Math.max(maxY, point.y);
|
||||
}
|
||||
return new WI.Rect(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
};
|
||||
|
||||
WI.CubicBezier = class CubicBezier
|
||||
{
|
||||
constructor(x1, y1, x2, y2)
|
||||
{
|
||||
this._inPoint = new WI.Point(x1, y1);
|
||||
this._outPoint = new WI.Point(x2, y2);
|
||||
|
||||
// Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
|
||||
this._curveInfo = {
|
||||
x: {c: 3.0 * x1},
|
||||
y: {c: 3.0 * y1}
|
||||
};
|
||||
|
||||
this._curveInfo.x.b = 3.0 * (x2 - x1) - this._curveInfo.x.c;
|
||||
this._curveInfo.x.a = 1.0 - this._curveInfo.x.c - this._curveInfo.x.b;
|
||||
|
||||
this._curveInfo.y.b = 3.0 * (y2 - y1) - this._curveInfo.y.c;
|
||||
this._curveInfo.y.a = 1.0 - this._curveInfo.y.c - this._curveInfo.y.b;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromCoordinates(coordinates)
|
||||
{
|
||||
if (!coordinates || coordinates.length < 4)
|
||||
return null;
|
||||
|
||||
coordinates = coordinates.map(Number);
|
||||
if (coordinates.includes(NaN))
|
||||
return null;
|
||||
|
||||
return new WI.CubicBezier(coordinates[0], coordinates[1], coordinates[2], coordinates[3]);
|
||||
}
|
||||
|
||||
static fromString(text)
|
||||
{
|
||||
if (!text || !text.length)
|
||||
return null;
|
||||
|
||||
var trimmedText = text.toLowerCase().replace(/\s/g, "");
|
||||
if (!trimmedText.length)
|
||||
return null;
|
||||
|
||||
if (Object.keys(WI.CubicBezier.keywordValues).includes(trimmedText))
|
||||
return WI.CubicBezier.fromCoordinates(WI.CubicBezier.keywordValues[trimmedText]);
|
||||
|
||||
var matches = trimmedText.match(/^cubic-bezier\(([-\d.]+),([-\d.]+),([-\d.]+),([-\d.]+)\)$/);
|
||||
if (!matches)
|
||||
return null;
|
||||
|
||||
matches.splice(0, 1);
|
||||
return WI.CubicBezier.fromCoordinates(matches);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get inPoint()
|
||||
{
|
||||
return this._inPoint;
|
||||
}
|
||||
|
||||
get outPoint()
|
||||
{
|
||||
return this._outPoint;
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.CubicBezier(this._inPoint.x, this._inPoint.y, this._outPoint.x, this._outPoint.y);
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
var values = [this._inPoint.x, this._inPoint.y, this._outPoint.x, this._outPoint.y];
|
||||
for (var key in WI.CubicBezier.keywordValues) {
|
||||
if (Array.shallowEqual(WI.CubicBezier.keywordValues[key], values))
|
||||
return key;
|
||||
}
|
||||
|
||||
return "cubic-bezier(" + values.join(", ") + ")";
|
||||
}
|
||||
|
||||
solve(x, epsilon)
|
||||
{
|
||||
return this._sampleCurveY(this._solveCurveX(x, epsilon));
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_sampleCurveX(t)
|
||||
{
|
||||
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
|
||||
return ((this._curveInfo.x.a * t + this._curveInfo.x.b) * t + this._curveInfo.x.c) * t;
|
||||
}
|
||||
|
||||
_sampleCurveY(t)
|
||||
{
|
||||
return ((this._curveInfo.y.a * t + this._curveInfo.y.b) * t + this._curveInfo.y.c) * t;
|
||||
}
|
||||
|
||||
_sampleCurveDerivativeX(t)
|
||||
{
|
||||
return (3.0 * this._curveInfo.x.a * t + 2.0 * this._curveInfo.x.b) * t + this._curveInfo.x.c;
|
||||
}
|
||||
|
||||
// Given an x value, find a parametric value it came from.
|
||||
_solveCurveX(x, epsilon)
|
||||
{
|
||||
var t0, t1, t2, x2, d2, i;
|
||||
|
||||
// First try a few iterations of Newton's method -- normally very fast.
|
||||
for (t2 = x, i = 0; i < 8; i++) {
|
||||
x2 = this._sampleCurveX(t2) - x;
|
||||
if (Math.abs(x2) < epsilon)
|
||||
return t2;
|
||||
d2 = this._sampleCurveDerivativeX(t2);
|
||||
if (Math.abs(d2) < 1e-6)
|
||||
break;
|
||||
t2 = t2 - x2 / d2;
|
||||
}
|
||||
|
||||
// Fall back to the bisection method for reliability.
|
||||
t0 = 0.0;
|
||||
t1 = 1.0;
|
||||
t2 = x;
|
||||
|
||||
if (t2 < t0)
|
||||
return t0;
|
||||
if (t2 > t1)
|
||||
return t1;
|
||||
|
||||
while (t0 < t1) {
|
||||
x2 = this._sampleCurveX(t2);
|
||||
if (Math.abs(x2 - x) < epsilon)
|
||||
return t2;
|
||||
if (x > x2)
|
||||
t0 = t2;
|
||||
else
|
||||
t1 = t2;
|
||||
t2 = (t1 - t0) * 0.5 + t0;
|
||||
}
|
||||
|
||||
// Failure.
|
||||
return t2;
|
||||
}
|
||||
};
|
||||
|
||||
WI.CubicBezier.keywordValues = {
|
||||
"ease": [0.25, 0.1, 0.25, 1],
|
||||
"ease-in": [0.42, 0, 1, 1],
|
||||
"ease-out": [0, 0, 0.58, 1],
|
||||
"ease-in-out": [0.42, 0, 0.58, 1],
|
||||
"linear": [0, 0, 1, 1]
|
||||
};
|
||||
|
||||
WI.Spring = class Spring
|
||||
{
|
||||
constructor(mass, stiffness, damping, initialVelocity)
|
||||
{
|
||||
this.mass = Math.max(1, mass);
|
||||
this.stiffness = Math.max(1, stiffness);
|
||||
this.damping = Math.max(0, damping);
|
||||
this.initialVelocity = initialVelocity;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromValues(values)
|
||||
{
|
||||
if (!values || values.length < 4)
|
||||
return null;
|
||||
|
||||
values = values.map(Number);
|
||||
if (values.includes(NaN))
|
||||
return null;
|
||||
|
||||
return new WI.Spring(...values);
|
||||
}
|
||||
|
||||
static fromString(text)
|
||||
{
|
||||
if (!text || !text.length)
|
||||
return null;
|
||||
|
||||
let trimmedText = text.toLowerCase().trim();
|
||||
if (!trimmedText.length)
|
||||
return null;
|
||||
|
||||
let matches = trimmedText.match(/^spring\(([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([-\d.]+)\)$/);
|
||||
if (!matches)
|
||||
return null;
|
||||
|
||||
return WI.Spring.fromValues(matches.slice(1));
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.Spring(this.mass, this.stiffness, this.damping, this.initialVelocity);
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
return `spring(${this.mass} ${this.stiffness} ${this.damping} ${this.initialVelocity})`;
|
||||
}
|
||||
|
||||
solve(t)
|
||||
{
|
||||
let w0 = Math.sqrt(this.stiffness / this.mass);
|
||||
let zeta = this.damping / (2 * Math.sqrt(this.stiffness * this.mass));
|
||||
|
||||
let wd = 0;
|
||||
let A = 1;
|
||||
let B = -this.initialVelocity + w0;
|
||||
if (zeta < 1) {
|
||||
// Under-damped.
|
||||
wd = w0 * Math.sqrt(1 - zeta * zeta);
|
||||
A = 1;
|
||||
B = (zeta * w0 + -this.initialVelocity) / wd;
|
||||
}
|
||||
|
||||
if (zeta < 1) // Under-damped
|
||||
t = Math.exp(-t * zeta * w0) * (A * Math.cos(wd * t) + B * Math.sin(wd * t));
|
||||
else // Critically damped (ignoring over-damped case).
|
||||
t = (A + B * t) * Math.exp(-t * w0);
|
||||
|
||||
return 1 - t; // Map range from [1..0] to [0..1].
|
||||
}
|
||||
|
||||
calculateDuration(epsilon)
|
||||
{
|
||||
epsilon = epsilon || 0.0001;
|
||||
let t = 0;
|
||||
let current = 0;
|
||||
let minimum = Number.POSITIVE_INFINITY;
|
||||
while (current >= epsilon || minimum >= epsilon) {
|
||||
current = Math.abs(1 - this.solve(t)); // Undo the range mapping
|
||||
if (minimum < epsilon && current >= epsilon)
|
||||
minimum = Number.POSITIVE_INFINITY; // Spring reversed direction
|
||||
else if (current < minimum)
|
||||
minimum = current;
|
||||
t += 0.1;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
WI.StepsFunction = class StepsFunction
|
||||
{
|
||||
constructor(type, count)
|
||||
{
|
||||
console.assert(Object.values(WI.StepsFunction.Type).includes(type), type);
|
||||
console.assert(count > 0, count);
|
||||
|
||||
this._type = type;
|
||||
this._count = count;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromString(text)
|
||||
{
|
||||
if (!text?.length)
|
||||
return null;
|
||||
|
||||
let trimmedText = text.toLowerCase().replace(/\s/g, "");
|
||||
if (!trimmedText.length)
|
||||
return null;
|
||||
|
||||
let keywordValue = WI.StepsFunction.keywordValues[trimmedText];
|
||||
if (keywordValue)
|
||||
return new WI.StepsFunction(...keywordValue);
|
||||
|
||||
let matches = trimmedText.match(/^steps\((\d+)(?:,([a-z-]+))?\)$/);
|
||||
if (!matches)
|
||||
return null;
|
||||
|
||||
let type = matches[2] || WI.StepsFunction.Type.JumpEnd;
|
||||
if (Object.values(WI.StepsFunction).includes(type))
|
||||
return null;
|
||||
|
||||
let count = Number(matches[1]);
|
||||
if (isNaN(count) || count <= 0)
|
||||
return null;
|
||||
|
||||
return new WI.StepsFunction(type, count);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get type() { return this._type; }
|
||||
get count() { return this._count; }
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.StepsFunction(this._type, this._count);
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
if (this._type === WI.StepsFunction.Type.JumpStart && this._count === 1)
|
||||
return "step-start";
|
||||
|
||||
if (this._type === WI.StepsFunction.Type.JumpEnd && this._count === 1)
|
||||
return "step-end";
|
||||
|
||||
return `steps(${this._count}, ${this._type})`;
|
||||
}
|
||||
};
|
||||
|
||||
WI.StepsFunction.Type = {
|
||||
JumpStart: "jump-start",
|
||||
JumpEnd: "jump-end",
|
||||
JumpNone: "jump-none",
|
||||
JumpBoth: "jump-both",
|
||||
Start: "start",
|
||||
End: "end",
|
||||
};
|
||||
|
||||
WI.StepsFunction.keywordValues = {
|
||||
"step-start": [WI.StepsFunction.Type.JumpStart, 1],
|
||||
"step-end": [WI.StepsFunction.Type.JumpEnd, 1],
|
||||
};
|
||||
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* Copyright (C) 2014, 2021 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Gradient = class Gradient
|
||||
{
|
||||
constructor(type, stops)
|
||||
{
|
||||
this.type = type;
|
||||
this.stops = stops;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static angleFromString(string)
|
||||
{
|
||||
let match = string.match(/([-\d\.]+)(\w+)/);
|
||||
if (!match || !Object.values(WI.Gradient.AngleUnits).includes(match[2]))
|
||||
return null;
|
||||
|
||||
return {value: parseFloat(match[1]), units: match[2]};
|
||||
}
|
||||
|
||||
static fromString(cssString)
|
||||
{
|
||||
var type;
|
||||
var openingParenthesisIndex = cssString.indexOf("(");
|
||||
var typeString = cssString.substring(0, openingParenthesisIndex);
|
||||
if (typeString.includes(WI.Gradient.Types.Linear))
|
||||
type = WI.Gradient.Types.Linear;
|
||||
else if (typeString.includes(WI.Gradient.Types.Radial))
|
||||
type = WI.Gradient.Types.Radial;
|
||||
else if (typeString.includes(WI.Gradient.Types.Conic))
|
||||
type = WI.Gradient.Types.Conic;
|
||||
else
|
||||
return null;
|
||||
|
||||
var components = [];
|
||||
var currentParams = [];
|
||||
var currentParam = "";
|
||||
var openParentheses = 0;
|
||||
var ch = openingParenthesisIndex + 1;
|
||||
var c = null;
|
||||
while (c = cssString[ch]) {
|
||||
if (c === "(")
|
||||
openParentheses++;
|
||||
if (c === ")")
|
||||
openParentheses--;
|
||||
|
||||
var isComma = c === ",";
|
||||
var isSpace = /\s/.test(c);
|
||||
|
||||
if (openParentheses === 0) {
|
||||
if (isSpace) {
|
||||
if (currentParam !== "")
|
||||
currentParams.push(currentParam);
|
||||
currentParam = "";
|
||||
} else if (isComma) {
|
||||
currentParams.push(currentParam);
|
||||
components.push(currentParams);
|
||||
currentParams = [];
|
||||
currentParam = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (openParentheses === -1) {
|
||||
currentParams.push(currentParam);
|
||||
components.push(currentParams);
|
||||
break;
|
||||
}
|
||||
|
||||
if (openParentheses > 0 || (!isComma && !isSpace))
|
||||
currentParam += c;
|
||||
|
||||
ch++;
|
||||
}
|
||||
|
||||
if (openParentheses !== -1)
|
||||
return null;
|
||||
|
||||
let gradient = null;
|
||||
switch (type) {
|
||||
case WI.Gradient.Types.Linear:
|
||||
gradient = WI.LinearGradient.fromComponents(components);
|
||||
break;
|
||||
|
||||
case WI.Gradient.Types.Radial:
|
||||
gradient = WI.RadialGradient.fromComponents(components);
|
||||
break;
|
||||
|
||||
case WI.Gradient.Types.Conic:
|
||||
gradient = WI.ConicGradient.fromComponents(components);
|
||||
break;
|
||||
}
|
||||
|
||||
if (gradient)
|
||||
gradient.repeats = typeString.startsWith("repeating");
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
static stopsWithComponents(components)
|
||||
{
|
||||
// FIXME: handle lengths.
|
||||
var stops = components.map(function(component) {
|
||||
while (component.length) {
|
||||
var color = WI.Color.fromString(component.shift());
|
||||
if (!color)
|
||||
continue;
|
||||
|
||||
var stop = {color};
|
||||
if (component.length && component[0].substr(-1) === "%")
|
||||
stop.offset = parseFloat(component.shift()) / 100;
|
||||
return stop;
|
||||
}
|
||||
});
|
||||
|
||||
if (!stops.length)
|
||||
return null;
|
||||
|
||||
for (var i = 0, count = stops.length; i < count; ++i) {
|
||||
var stop = stops[i];
|
||||
|
||||
// If one of the stops failed to parse, then this is not a valid
|
||||
// set of components for a gradient. So the whole thing is invalid.
|
||||
if (!stop)
|
||||
return null;
|
||||
|
||||
if (!stop.offset)
|
||||
stop.offset = i / (count - 1);
|
||||
}
|
||||
|
||||
return stops;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
stringFromStops(stops)
|
||||
{
|
||||
var count = stops.length - 1;
|
||||
return stops.map(function(stop, index) {
|
||||
var str = stop.color;
|
||||
if (stop.offset !== index / count)
|
||||
str += " " + Math.round(stop.offset * 10_000) / 100 + "%";
|
||||
return str;
|
||||
}).join(", ");
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get angleValue()
|
||||
{
|
||||
return this._angle.value.maxDecimals(2);
|
||||
}
|
||||
|
||||
set angleValue(value)
|
||||
{
|
||||
this._angle.value = value;
|
||||
}
|
||||
|
||||
get angleUnits()
|
||||
{
|
||||
return this._angle.units;
|
||||
}
|
||||
|
||||
set angleUnits(units)
|
||||
{
|
||||
if (units === this._angle.units)
|
||||
return;
|
||||
|
||||
this._angle.value = this._angleValueForUnits(units);
|
||||
this._angle.units = units;
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
// Implemented by subclasses.
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
// Implemented by subclasses.
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_angleValueForUnits(units)
|
||||
{
|
||||
if (units === this._angle.units)
|
||||
return this._angle.value;
|
||||
|
||||
let deg = 0;
|
||||
|
||||
switch (this._angle.units) {
|
||||
case WI.Gradient.AngleUnits.DEG:
|
||||
deg = this._angle.value;
|
||||
break;
|
||||
|
||||
case WI.Gradient.AngleUnits.RAD:
|
||||
deg = this._angle.value * 180 / Math.PI;
|
||||
break;
|
||||
|
||||
case WI.Gradient.AngleUnits.GRAD:
|
||||
deg = this._angle.value / 400 * 360;
|
||||
break;
|
||||
|
||||
case WI.Gradient.AngleUnits.TURN:
|
||||
deg = this._angle.value * 360;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (units) {
|
||||
case WI.Gradient.AngleUnits.DEG:
|
||||
return deg;
|
||||
|
||||
case WI.Gradient.AngleUnits.RAD:
|
||||
return deg * Math.PI / 180;
|
||||
|
||||
case WI.Gradient.AngleUnits.GRAD:
|
||||
return deg / 360 * 400;
|
||||
|
||||
case WI.Gradient.AngleUnits.TURN:
|
||||
return deg / 360;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
WI.Gradient.Types = {
|
||||
Linear: "linear-gradient",
|
||||
Radial: "radial-gradient",
|
||||
Conic: "conic-gradient",
|
||||
};
|
||||
|
||||
WI.Gradient.AngleUnits = {
|
||||
DEG: "deg",
|
||||
RAD: "rad",
|
||||
GRAD: "grad",
|
||||
TURN: "turn",
|
||||
};
|
||||
|
||||
WI.LinearGradient = class LinearGradient extends WI.Gradient
|
||||
{
|
||||
constructor(angle, stops)
|
||||
{
|
||||
super(WI.Gradient.Types.Linear, stops);
|
||||
this._angle = angle;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromComponents(components)
|
||||
{
|
||||
let angle = {value: 180, units: WI.Gradient.AngleUnits.DEG};
|
||||
|
||||
if (components[0].length === 1 && !WI.Color.fromString(components[0][0])) {
|
||||
angle = WI.Gradient.angleFromString(components[0][0]);
|
||||
|
||||
if (!angle)
|
||||
return null;
|
||||
|
||||
components.shift();
|
||||
} else if (components[0][0] === "to") {
|
||||
components[0].shift();
|
||||
switch (components[0].sort().join(" ")) {
|
||||
case "top":
|
||||
angle.value = 0;
|
||||
break;
|
||||
case "right top":
|
||||
angle.value = 45;
|
||||
break;
|
||||
case "right":
|
||||
angle.value = 90;
|
||||
break;
|
||||
case "bottom right":
|
||||
angle.value = 135;
|
||||
break;
|
||||
case "bottom":
|
||||
angle.value = 180;
|
||||
break;
|
||||
case "bottom left":
|
||||
angle.value = 225;
|
||||
break;
|
||||
case "left":
|
||||
angle.value = 270;
|
||||
break;
|
||||
case "left top":
|
||||
angle.value = 315;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
components.shift();
|
||||
} else if (components[0].length !== 1 && !WI.Color.fromString(components[0][0])) {
|
||||
// If the first component is not a color, then we're dealing with a
|
||||
// legacy linear gradient format that we don't support.
|
||||
return null;
|
||||
}
|
||||
|
||||
let stops = WI.Gradient.stopsWithComponents(components);
|
||||
if (!stops)
|
||||
return null;
|
||||
|
||||
return new WI.LinearGradient(angle, stops);
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.LinearGradient(this._angle, this.stops.concat());
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
let str = "";
|
||||
|
||||
let deg = this._angleValueForUnits(WI.LinearGradient.AngleUnits.DEG);
|
||||
if (deg === 0)
|
||||
str += "to top";
|
||||
else if (deg === 45)
|
||||
str += "to top right";
|
||||
else if (deg === 90)
|
||||
str += "to right";
|
||||
else if (deg === 135)
|
||||
str += "to bottom right";
|
||||
else if (deg === 225)
|
||||
str += "to bottom left";
|
||||
else if (deg === 270)
|
||||
str += "to left";
|
||||
else if (deg === 315)
|
||||
str += "to top left";
|
||||
else if (deg !== 180)
|
||||
str += this.angleValue + this.angleUnits;
|
||||
|
||||
if (str)
|
||||
str += ", ";
|
||||
|
||||
str += this.stringFromStops(this.stops);
|
||||
|
||||
return (this.repeats ? "repeating-" : "") + this.type + "(" + str + ")";
|
||||
}
|
||||
};
|
||||
|
||||
WI.RadialGradient = class RadialGradient extends WI.Gradient
|
||||
{
|
||||
constructor(sizing, stops)
|
||||
{
|
||||
super(WI.Gradient.Types.Radial, stops);
|
||||
this.sizing = sizing;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromComponents(components)
|
||||
{
|
||||
let sizing = !WI.Color.fromString(components[0].join(" ")) ? components.shift().join(" ") : "";
|
||||
|
||||
let stops = WI.Gradient.stopsWithComponents(components);
|
||||
if (!stops)
|
||||
return null;
|
||||
|
||||
return new WI.RadialGradient(sizing, stops);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get angleValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
set angleValue(value)
|
||||
{
|
||||
console.assert(false, "CSS radial gradients do not have an angle");
|
||||
}
|
||||
|
||||
get angleUnits()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
set angleUnits(units)
|
||||
{
|
||||
console.assert(false, "CSS radial gradients do not have an angle");
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.RadialGradient(this.sizing, this.stops.concat());
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
let str = this.sizing;
|
||||
|
||||
if (str)
|
||||
str += ", ";
|
||||
|
||||
str += this.stringFromStops(this.stops);
|
||||
|
||||
return (this.repeats ? "repeating-" : "") + this.type + "(" + str + ")";
|
||||
}
|
||||
};
|
||||
|
||||
WI.ConicGradient = class ConicGradient extends WI.Gradient
|
||||
{
|
||||
constructor(angle, position, stops)
|
||||
{
|
||||
super(WI.Gradient.Types.Conic, stops);
|
||||
|
||||
this._angle = angle;
|
||||
this._position = position;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromComponents(components)
|
||||
{
|
||||
let angle = {value: 0, units: WI.Gradient.AngleUnits.DEG};
|
||||
let position = null;
|
||||
let hasCustomAngleOrPosition = false;
|
||||
|
||||
if (components[0][0] == "from") {
|
||||
components[0].shift();
|
||||
angle = WI.Gradient.angleFromString(components[0][0]);
|
||||
if (!angle)
|
||||
return null;
|
||||
components[0].shift();
|
||||
hasCustomAngleOrPosition = true;
|
||||
}
|
||||
if (components[0][0] == "at") {
|
||||
components[0].shift();
|
||||
// FIXME: <https://webkit.org/b/234643> (Web Inspector: allow editing positions in gradient editor)
|
||||
if (components[0].length <= 0)
|
||||
return null;
|
||||
position = components[0].join(" ");
|
||||
hasCustomAngleOrPosition = true;
|
||||
}
|
||||
if (hasCustomAngleOrPosition)
|
||||
components.shift();
|
||||
|
||||
let stops = WI.Gradient.stopsWithComponents(components);
|
||||
if (!stops)
|
||||
return null;
|
||||
|
||||
return new WI.ConicGradient(angle, position, stops);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.ConicGradient(this._angle, this._position, this.stops.concat());
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
let str = "";
|
||||
|
||||
if (this._angle.value)
|
||||
str += `from ${this._angle.value}${this._angle.units}`;
|
||||
|
||||
if (this._position) {
|
||||
if (str)
|
||||
str += " ";
|
||||
str += `at ${this._position}`;
|
||||
}
|
||||
|
||||
if (str)
|
||||
str += ", ";
|
||||
|
||||
str += this.stringFromStops(this.stops);
|
||||
|
||||
return (this.repeats ? "repeating-" : "") + this.type + "(" + str + ")";
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.HeapAllocationsInstrument = class HeapAllocationsInstrument extends WI.Instrument
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
|
||||
this._snapshotIntervalIdentifier = undefined;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
get timelineRecordType()
|
||||
{
|
||||
return WI.TimelineRecord.Type.HeapAllocations;
|
||||
}
|
||||
|
||||
startInstrumentation(initiatedByBackend)
|
||||
{
|
||||
// FIXME: Include a "track allocations" option for this instrument.
|
||||
// FIXME: Include a periodic snapshot interval option for this instrument.
|
||||
|
||||
if (!initiatedByBackend) {
|
||||
let target = WI.assumingMainTarget();
|
||||
target.HeapAgent.startTracking();
|
||||
}
|
||||
|
||||
// Periodic snapshots.
|
||||
const snapshotInterval = 10_000;
|
||||
this._snapshotIntervalIdentifier = setInterval(this._takeHeapSnapshot.bind(this), snapshotInterval);
|
||||
}
|
||||
|
||||
stopInstrumentation(initiatedByBackend)
|
||||
{
|
||||
if (!initiatedByBackend) {
|
||||
let target = WI.assumingMainTarget();
|
||||
target.HeapAgent.stopTracking();
|
||||
}
|
||||
|
||||
window.clearInterval(this._snapshotIntervalIdentifier);
|
||||
this._snapshotIntervalIdentifier = undefined;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_takeHeapSnapshot()
|
||||
{
|
||||
WI.heapManager.snapshot((error, timestamp, snapshotStringData) => {
|
||||
let workerProxy = WI.HeapSnapshotWorkerProxy.singleton();
|
||||
workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => {
|
||||
let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
|
||||
snapshot.snapshotStringData = snapshotStringData;
|
||||
WI.timelineManager.heapSnapshotAdded(timestamp, snapshot);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.HeapAllocationsTimelineRecord = class HeapAllocationsTimelineRecord extends WI.TimelineRecord
|
||||
{
|
||||
constructor(timestamp, heapSnapshot)
|
||||
{
|
||||
super(WI.TimelineRecord.Type.HeapAllocations, timestamp, timestamp);
|
||||
|
||||
console.assert(typeof timestamp === "number");
|
||||
console.assert(heapSnapshot instanceof WI.HeapSnapshotProxy);
|
||||
|
||||
this._timestamp = timestamp;
|
||||
this._heapSnapshot = heapSnapshot;
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static async fromJSON(json)
|
||||
{
|
||||
// NOTE: This just goes through and generates a new heap snapshot,
|
||||
// it is not perfect but does what we want. It asynchronously creates
|
||||
// a heap snapshot at the right time, and insert it into the active
|
||||
// recording, which on an import should be the newly imported recording.
|
||||
let {timestamp, title, snapshotStringData} = json;
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
let workerProxy = WI.HeapSnapshotWorkerProxy.singleton();
|
||||
workerProxy.createImportedSnapshot(snapshotStringData, title, ({objectId, snapshot: serializedSnapshot}) => {
|
||||
let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
|
||||
snapshot.snapshotStringData = snapshotStringData;
|
||||
resolve(new WI.HeapAllocationsTimelineRecord(timestamp, snapshot));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return {
|
||||
type: this.type,
|
||||
timestamp: this._timestamp,
|
||||
title: WI.TimelineTabContentView.displayNameForRecord(this),
|
||||
snapshotStringData: this._heapSnapshot.snapshotStringData,
|
||||
};
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get timestamp() { return this._timestamp; }
|
||||
get heapSnapshot() { return this._heapSnapshot; }
|
||||
};
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.HeapSnapshotRootPath = class HeapSnapshotRootPath
|
||||
{
|
||||
constructor(node, pathComponent, parent, isGlobalScope)
|
||||
{
|
||||
console.assert(!node || node instanceof WI.HeapSnapshotNodeProxy);
|
||||
console.assert(!pathComponent || typeof pathComponent === "string");
|
||||
console.assert(!parent || parent instanceof WI.HeapSnapshotRootPath);
|
||||
|
||||
this._node = node || null;
|
||||
this._parent = parent || null;
|
||||
this._pathComponent = typeof pathComponent === "string" ? pathComponent : null;
|
||||
this._isGlobalScope = isGlobalScope || false;
|
||||
|
||||
// Become the new root when appended to an empty path.
|
||||
if (this._parent && this._parent.isEmpty())
|
||||
this._parent = null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static emptyPath()
|
||||
{
|
||||
return new WI.HeapSnapshotRootPath(null);
|
||||
}
|
||||
|
||||
static pathComponentForIndividualEdge(edge)
|
||||
{
|
||||
switch (edge.type) {
|
||||
case WI.HeapSnapshotEdgeProxy.EdgeType.Internal:
|
||||
return null;
|
||||
case WI.HeapSnapshotEdgeProxy.EdgeType.Index:
|
||||
return "[" + edge.data + "]";
|
||||
case WI.HeapSnapshotEdgeProxy.EdgeType.Property:
|
||||
case WI.HeapSnapshotEdgeProxy.EdgeType.Variable:
|
||||
if (WI.HeapSnapshotRootPath.canPropertyNameBeDotAccess(edge.data))
|
||||
return edge.data;
|
||||
return "[" + doubleQuotedString(edge.data) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
static canPropertyNameBeDotAccess(propertyName)
|
||||
{
|
||||
return /^(?![0-9])\w+$/.test(propertyName);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get node() { return this._node; }
|
||||
get parent() { return this._parent; }
|
||||
get pathComponent() { return this._pathComponent; }
|
||||
|
||||
get rootNode()
|
||||
{
|
||||
return this._parent ? this._parent.rootNode : this._node;
|
||||
}
|
||||
|
||||
get fullPath()
|
||||
{
|
||||
let components = [];
|
||||
for (let p = this; p && p.pathComponent; p = p.parent)
|
||||
components.push(p.pathComponent);
|
||||
components.reverse();
|
||||
return components.join("");
|
||||
}
|
||||
|
||||
isRoot()
|
||||
{
|
||||
return !this._parent;
|
||||
}
|
||||
|
||||
isEmpty()
|
||||
{
|
||||
return !this._node;
|
||||
}
|
||||
|
||||
isGlobalScope()
|
||||
{
|
||||
return this._isGlobalScope;
|
||||
}
|
||||
|
||||
isPathComponentImpossible()
|
||||
{
|
||||
return this._pathComponent && this._pathComponent.startsWith("@");
|
||||
}
|
||||
|
||||
isFullPathImpossible()
|
||||
{
|
||||
if (this.isEmpty())
|
||||
return true;
|
||||
|
||||
if (this.isPathComponentImpossible())
|
||||
return true;
|
||||
|
||||
if (this._parent)
|
||||
return this._parent.isFullPathImpossible();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
appendInternal(node)
|
||||
{
|
||||
return new WI.HeapSnapshotRootPath(node, WI.HeapSnapshotRootPath.SpecialPathComponent.InternalPropertyName, this);
|
||||
}
|
||||
|
||||
appendArrayIndex(node, index)
|
||||
{
|
||||
let component = "[" + index + "]";
|
||||
return new WI.HeapSnapshotRootPath(node, component, this);
|
||||
}
|
||||
|
||||
appendPropertyName(node, propertyName)
|
||||
{
|
||||
let component = WI.HeapSnapshotRootPath.canPropertyNameBeDotAccess(propertyName) ? "." + propertyName : "[" + doubleQuotedString(propertyName) + "]";
|
||||
return new WI.HeapSnapshotRootPath(node, component, this);
|
||||
}
|
||||
|
||||
appendVariableName(node, variableName)
|
||||
{
|
||||
// Treat as a property of the global object, e.g. "window.foo".
|
||||
if (this._isGlobalScope)
|
||||
return this.appendPropertyName(node, variableName);
|
||||
return new WI.HeapSnapshotRootPath(node, variableName, this);
|
||||
}
|
||||
|
||||
appendGlobalScopeName(node, globalScopeName)
|
||||
{
|
||||
return new WI.HeapSnapshotRootPath(node, globalScopeName, this, true);
|
||||
}
|
||||
|
||||
appendEdge(edge)
|
||||
{
|
||||
console.assert(edge instanceof WI.HeapSnapshotEdgeProxy);
|
||||
|
||||
switch (edge.type) {
|
||||
case WI.HeapSnapshotEdgeProxy.EdgeType.Internal:
|
||||
return this.appendInternal(edge.to);
|
||||
case WI.HeapSnapshotEdgeProxy.EdgeType.Index:
|
||||
return this.appendArrayIndex(edge.to, edge.data);
|
||||
case WI.HeapSnapshotEdgeProxy.EdgeType.Property:
|
||||
return this.appendPropertyName(edge.to, edge.data);
|
||||
case WI.HeapSnapshotEdgeProxy.EdgeType.Variable:
|
||||
return this.appendVariableName(edge.to, edge.data);
|
||||
}
|
||||
|
||||
console.error("Unexpected edge type", edge.type);
|
||||
}
|
||||
};
|
||||
|
||||
WI.HeapSnapshotRootPath.SpecialPathComponent = {
|
||||
InternalPropertyName: "@internal",
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.IndexedDatabase = class IndexedDatabase
|
||||
{
|
||||
constructor(name, securityOrigin, version, objectStores)
|
||||
{
|
||||
this._name = name;
|
||||
this._securityOrigin = securityOrigin;
|
||||
this._host = parseSecurityOrigin(securityOrigin).host;
|
||||
this._version = version;
|
||||
this._objectStores = objectStores || [];
|
||||
|
||||
for (var objectStore of this._objectStores)
|
||||
objectStore.establishRelationship(this);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get name() { return this._name; }
|
||||
get securityOrigin() { return this._securityOrigin; }
|
||||
get host() { return this._host; }
|
||||
get version() { return this._version; }
|
||||
get objectStores() { return this._objectStores; }
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie[WI.IndexedDatabase.NameCookieKey] = this._name;
|
||||
cookie[WI.IndexedDatabase.HostCookieKey] = this._host;
|
||||
}
|
||||
};
|
||||
|
||||
WI.IndexedDatabase.TypeIdentifier = "indexed-database";
|
||||
WI.IndexedDatabase.NameCookieKey = "indexed-database-name";
|
||||
WI.IndexedDatabase.HostCookieKey = "indexed-database-host";
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.IndexedDatabaseObjectStore = class IndexedDatabaseObjectStore
|
||||
{
|
||||
constructor(name, keyPath, autoIncrement, indexes)
|
||||
{
|
||||
this._name = name;
|
||||
this._keyPath = keyPath;
|
||||
this._autoIncrement = autoIncrement || false;
|
||||
this._indexes = indexes || [];
|
||||
this._parentDatabase = null;
|
||||
|
||||
for (var index of this._indexes)
|
||||
index.establishRelationship(this);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get name() { return this._name; }
|
||||
get keyPath() { return this._keyPath; }
|
||||
get autoIncrement() { return this._autoIncrement; }
|
||||
get parentDatabase() { return this._parentDatabase; }
|
||||
get indexes() { return this._indexes; }
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie[WI.IndexedDatabaseObjectStore.NameCookieKey] = this._name;
|
||||
cookie[WI.IndexedDatabaseObjectStore.KeyPathCookieKey] = this._keyPath;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
establishRelationship(parentDatabase)
|
||||
{
|
||||
this._parentDatabase = parentDatabase || null;
|
||||
}
|
||||
};
|
||||
|
||||
WI.IndexedDatabaseObjectStore.TypeIdentifier = "indexed-database-object-store";
|
||||
WI.IndexedDatabaseObjectStore.NameCookieKey = "indexed-database-object-store-name";
|
||||
WI.IndexedDatabaseObjectStore.KeyPathCookieKey = "indexed-database-object-store-key-path";
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.IndexedDatabaseObjectStoreIndex = class IndexedDatabaseObjectStoreIndex
|
||||
{
|
||||
constructor(name, keyPath, unique, multiEntry)
|
||||
{
|
||||
this._name = name;
|
||||
this._keyPath = keyPath;
|
||||
this._unique = unique || false;
|
||||
this._multiEntry = multiEntry || false;
|
||||
this._parentObjectStore = null;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get name() { return this._name; }
|
||||
get keyPath() { return this._keyPath; }
|
||||
get unique() { return this._unique; }
|
||||
get multiEntry() { return this._multiEntry; }
|
||||
get parentObjectStore() { return this._parentObjectStore; }
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie[WI.IndexedDatabaseObjectStoreIndex.NameCookieKey] = this._name;
|
||||
cookie[WI.IndexedDatabaseObjectStoreIndex.KeyPathCookieKey] = this._keyPath;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
establishRelationship(parentObjectStore)
|
||||
{
|
||||
this._parentObjectStore = parentObjectStore || null;
|
||||
}
|
||||
};
|
||||
|
||||
WI.IndexedDatabaseObjectStoreIndex.TypeIdentifier = "indexed-database-object-store-index";
|
||||
WI.IndexedDatabaseObjectStoreIndex.NameCookieKey = "indexed-database-object-store-index-name";
|
||||
WI.IndexedDatabaseObjectStoreIndex.KeyPathCookieKey = "indexed-database-object-store-index-key-path";
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Instrument = class Instrument
|
||||
{
|
||||
// Static
|
||||
|
||||
static createForTimelineType(type)
|
||||
{
|
||||
switch (type) {
|
||||
case WI.TimelineRecord.Type.Network:
|
||||
return new WI.NetworkInstrument;
|
||||
case WI.TimelineRecord.Type.Layout:
|
||||
return new WI.LayoutInstrument;
|
||||
case WI.TimelineRecord.Type.Script:
|
||||
return new WI.ScriptInstrument;
|
||||
case WI.TimelineRecord.Type.RenderingFrame:
|
||||
return new WI.FPSInstrument;
|
||||
case WI.TimelineRecord.Type.CPU:
|
||||
return new WI.CPUInstrument;
|
||||
case WI.TimelineRecord.Type.Memory:
|
||||
return new WI.MemoryInstrument;
|
||||
case WI.TimelineRecord.Type.HeapAllocations:
|
||||
return new WI.HeapAllocationsInstrument;
|
||||
case WI.TimelineRecord.Type.Media:
|
||||
return new WI.MediaInstrument;
|
||||
case WI.TimelineRecord.Type.Screenshots:
|
||||
return new WI.ScreenshotsInstrument;
|
||||
default:
|
||||
console.error("Unknown TimelineRecord.Type: " + type);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static startLegacyTimelineAgent(initiatedByBackend)
|
||||
{
|
||||
console.assert(WI.timelineManager._enabled);
|
||||
|
||||
if (WI.Instrument._legacyTimelineAgentStarted)
|
||||
return;
|
||||
|
||||
WI.Instrument._legacyTimelineAgentStarted = true;
|
||||
|
||||
if (initiatedByBackend)
|
||||
return;
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
target.TimelineAgent.start();
|
||||
}
|
||||
|
||||
static stopLegacyTimelineAgent(initiatedByBackend)
|
||||
{
|
||||
console.assert(WI.timelineManager._enabled);
|
||||
|
||||
if (!WI.Instrument._legacyTimelineAgentStarted)
|
||||
return;
|
||||
|
||||
WI.Instrument._legacyTimelineAgentStarted = false;
|
||||
|
||||
if (initiatedByBackend)
|
||||
return;
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
target.TimelineAgent.stop();
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
get timelineRecordType()
|
||||
{
|
||||
return null; // Implemented by subclasses.
|
||||
}
|
||||
|
||||
startInstrumentation(initiatedByBackend)
|
||||
{
|
||||
WI.Instrument.startLegacyTimelineAgent(initiatedByBackend);
|
||||
}
|
||||
|
||||
stopInstrumentation(initiatedByBackend)
|
||||
{
|
||||
WI.Instrument.stopLegacyTimelineAgent(initiatedByBackend);
|
||||
}
|
||||
};
|
||||
|
||||
WI.Instrument._legacyTimelineAgentStarted = false;
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.IssueMessage = class IssueMessage extends WI.Object
|
||||
{
|
||||
constructor(consoleMessage)
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(consoleMessage instanceof WI.ConsoleMessage);
|
||||
|
||||
this._consoleMessage = consoleMessage;
|
||||
|
||||
this._text = this._issueText();
|
||||
|
||||
switch (this._consoleMessage.source) {
|
||||
case WI.ConsoleMessage.MessageSource.JS:
|
||||
// FIXME: It would be nice if we had this information (the specific type of JavaScript error)
|
||||
// as part of the data passed from WebCore, instead of having to determine it ourselves.
|
||||
var prefixRegex = /^([^:]+): (?:DOM Exception \d+: )?/;
|
||||
var match = prefixRegex.exec(this._text);
|
||||
if (match && match[1] in WI.IssueMessage.Type._prefixTypeMap) {
|
||||
this._type = WI.IssueMessage.Type._prefixTypeMap[match[1]];
|
||||
this._text = this._text.substring(match[0].length);
|
||||
} else
|
||||
this._type = WI.IssueMessage.Type.OtherIssue;
|
||||
break;
|
||||
|
||||
case WI.ConsoleMessage.MessageSource.CSS:
|
||||
case WI.ConsoleMessage.MessageSource.XML:
|
||||
this._type = WI.IssueMessage.Type.PageIssue;
|
||||
break;
|
||||
|
||||
case WI.ConsoleMessage.MessageSource.Network:
|
||||
this._type = WI.IssueMessage.Type.NetworkIssue;
|
||||
break;
|
||||
|
||||
case WI.ConsoleMessage.MessageSource.Security:
|
||||
this._type = WI.IssueMessage.Type.SecurityIssue;
|
||||
break;
|
||||
|
||||
case WI.ConsoleMessage.MessageSource.ConsoleAPI:
|
||||
case WI.ConsoleMessage.MessageSource.Storage:
|
||||
case WI.ConsoleMessage.MessageSource.Appcache:
|
||||
case WI.ConsoleMessage.MessageSource.Rendering:
|
||||
case WI.ConsoleMessage.MessageSource.Media:
|
||||
case WI.ConsoleMessage.MessageSource.Mediasource:
|
||||
case WI.ConsoleMessage.MessageSource.WebRTC:
|
||||
case WI.ConsoleMessage.MessageSource.ITPDebug:
|
||||
case WI.ConsoleMessage.MessageSource.PrivateClickMeasurement:
|
||||
case WI.ConsoleMessage.MessageSource.PaymentRequest:
|
||||
case WI.ConsoleMessage.MessageSource.AdClickAttribution: // COMPATIBILITY (iOS 14.0): `Console.ChannelSource.AdClickAttribution` was renamed to `Console.ChannelSource.PrivateClickMeasurement`.
|
||||
case WI.ConsoleMessage.MessageSource.Other:
|
||||
this._type = WI.IssueMessage.Type.OtherIssue;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error("Unknown issue source:", this._consoleMessage.source);
|
||||
this._type = WI.IssueMessage.Type.OtherIssue;
|
||||
}
|
||||
|
||||
this._sourceCodeLocation = consoleMessage.sourceCodeLocation;
|
||||
if (this._sourceCodeLocation)
|
||||
this._sourceCodeLocation.addEventListener(WI.SourceCodeLocation.Event.DisplayLocationChanged, this._sourceCodeLocationDisplayLocationChanged, this);
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static displayName(type)
|
||||
{
|
||||
switch (type) {
|
||||
case WI.IssueMessage.Type.SemanticIssue:
|
||||
return WI.UIString("Semantic Issue");
|
||||
case WI.IssueMessage.Type.RangeIssue:
|
||||
return WI.UIString("Range Issue");
|
||||
case WI.IssueMessage.Type.ReferenceIssue:
|
||||
return WI.UIString("Reference Issue");
|
||||
case WI.IssueMessage.Type.TypeIssue:
|
||||
return WI.UIString("Type Issue");
|
||||
case WI.IssueMessage.Type.PageIssue:
|
||||
return WI.UIString("Page Issue");
|
||||
case WI.IssueMessage.Type.NetworkIssue:
|
||||
return WI.UIString("Network Issue");
|
||||
case WI.IssueMessage.Type.SecurityIssue:
|
||||
return WI.UIString("Security Issue");
|
||||
case WI.IssueMessage.Type.OtherIssue:
|
||||
return WI.UIString("Other Issue");
|
||||
default:
|
||||
console.error("Unknown issue message type:", type);
|
||||
return WI.UIString("Other Issue");
|
||||
}
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get text() { return this._text; }
|
||||
get type() { return this._type; }
|
||||
get level() { return this._consoleMessage.level; }
|
||||
get source() { return this._consoleMessage.source; }
|
||||
get url() { return this._consoleMessage.url; }
|
||||
get sourceCodeLocation() { return this._sourceCodeLocation; }
|
||||
|
||||
// Protected
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie[WI.IssueMessage.URLCookieKey] = this.url;
|
||||
cookie[WI.IssueMessage.LineNumberCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.lineNumber : 0;
|
||||
cookie[WI.IssueMessage.ColumnNumberCookieKey] = this._sourceCodeLocation ? this._sourceCodeLocation.columnNumber : 0;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_issueText()
|
||||
{
|
||||
let parameters = this._consoleMessage.parameters;
|
||||
if (!parameters)
|
||||
return this._consoleMessage.messageText;
|
||||
|
||||
if (parameters[0].type !== "string")
|
||||
return this._consoleMessage.messageText;
|
||||
|
||||
function valueFormatter(obj)
|
||||
{
|
||||
return obj.description;
|
||||
}
|
||||
|
||||
let formatters = {};
|
||||
formatters.o = valueFormatter;
|
||||
formatters.s = valueFormatter;
|
||||
formatters.f = valueFormatter;
|
||||
formatters.i = valueFormatter;
|
||||
formatters.d = valueFormatter;
|
||||
|
||||
function append(a, b)
|
||||
{
|
||||
a += b;
|
||||
return a;
|
||||
}
|
||||
|
||||
let result = String.format(parameters[0].description, parameters.slice(1), formatters, "", append);
|
||||
let resultText = result.formattedResult;
|
||||
|
||||
for (let i = 0; i < result.unusedSubstitutions.length; ++i)
|
||||
resultText += " " + result.unusedSubstitutions[i].description;
|
||||
|
||||
return resultText;
|
||||
}
|
||||
|
||||
_sourceCodeLocationDisplayLocationChanged(event)
|
||||
{
|
||||
this.dispatchEventToListeners(WI.IssueMessage.Event.DisplayLocationDidChange, event.data);
|
||||
}
|
||||
};
|
||||
|
||||
WI.IssueMessage.Level = {
|
||||
Error: "error",
|
||||
Warning: "warning"
|
||||
};
|
||||
|
||||
WI.IssueMessage.Type = {
|
||||
SemanticIssue: "issue-message-type-semantic-issue",
|
||||
RangeIssue: "issue-message-type-range-issue",
|
||||
ReferenceIssue: "issue-message-type-reference-issue",
|
||||
TypeIssue: "issue-message-type-type-issue",
|
||||
PageIssue: "issue-message-type-page-issue",
|
||||
NetworkIssue: "issue-message-type-network-issue",
|
||||
SecurityIssue: "issue-message-type-security-issue",
|
||||
OtherIssue: "issue-message-type-other-issue"
|
||||
};
|
||||
|
||||
WI.IssueMessage.TypeIdentifier = "issue-message";
|
||||
WI.IssueMessage.URLCookieKey = "issue-message-url";
|
||||
WI.IssueMessage.LineNumberCookieKey = "issue-message-line-number";
|
||||
WI.IssueMessage.ColumnNumberCookieKey = "issue-message-column-number";
|
||||
|
||||
WI.IssueMessage.Event = {
|
||||
LocationDidChange: "issue-message-location-did-change",
|
||||
DisplayLocationDidChange: "issue-message-display-location-did-change"
|
||||
};
|
||||
|
||||
WI.IssueMessage.Type._prefixTypeMap = {
|
||||
"SyntaxError": WI.IssueMessage.Type.SemanticIssue,
|
||||
"URIError": WI.IssueMessage.Type.SemanticIssue,
|
||||
"AggregateError": WI.IssueMessage.Type.SemanticIssue,
|
||||
"EvalError": WI.IssueMessage.Type.SemanticIssue,
|
||||
"INVALID_CHARACTER_ERR": WI.IssueMessage.Type.SemanticIssue,
|
||||
"SYNTAX_ERR": WI.IssueMessage.Type.SemanticIssue,
|
||||
|
||||
"RangeError": WI.IssueMessage.Type.RangeIssue,
|
||||
"INDEX_SIZE_ERR": WI.IssueMessage.Type.RangeIssue,
|
||||
"DOMSTRING_SIZE_ERR": WI.IssueMessage.Type.RangeIssue,
|
||||
|
||||
"ReferenceError": WI.IssueMessage.Type.ReferenceIssue,
|
||||
"HIERARCHY_REQUEST_ERR": WI.IssueMessage.Type.ReferenceIssue,
|
||||
"INVALID_STATE_ERR": WI.IssueMessage.Type.ReferenceIssue,
|
||||
"NOT_FOUND_ERR": WI.IssueMessage.Type.ReferenceIssue,
|
||||
"WRONG_DOCUMENT_ERR": WI.IssueMessage.Type.ReferenceIssue,
|
||||
|
||||
"TypeError": WI.IssueMessage.Type.TypeIssue,
|
||||
"INVALID_NODE_TYPE_ERR": WI.IssueMessage.Type.TypeIssue,
|
||||
"TYPE_MISMATCH_ERR": WI.IssueMessage.Type.TypeIssue,
|
||||
|
||||
"SECURITY_ERR": WI.IssueMessage.Type.SecurityIssue,
|
||||
|
||||
"NETWORK_ERR": WI.IssueMessage.Type.NetworkIssue,
|
||||
|
||||
"ABORT_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"DATA_CLONE_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"INUSE_ATTRIBUTE_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"INVALID_ACCESS_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"INVALID_MODIFICATION_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"NAMESPACE_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"NOT_SUPPORTED_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"NO_DATA_ALLOWED_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"NO_MODIFICATION_ALLOWED_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"QUOTA_EXCEEDED_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"TIMEOUT_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"URL_MISMATCH_ERR": WI.IssueMessage.Type.OtherIssue,
|
||||
"VALIDATION_ERR": WI.IssueMessage.Type.OtherIssue
|
||||
};
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.JavaScriptBreakpoint = class JavaScriptBreakpoint extends WI.Breakpoint
|
||||
{
|
||||
constructor(sourceCodeLocation, {contentIdentifier, resolved, disabled, condition, actions, ignoreCount, autoContinue} = {})
|
||||
{
|
||||
console.assert(sourceCodeLocation instanceof WI.SourceCodeLocation, sourceCodeLocation);
|
||||
console.assert(!contentIdentifier || typeof contentIdentifier === "string", contentIdentifier);
|
||||
console.assert(resolved === undefined || typeof resolved === "boolean", resolved);
|
||||
|
||||
super({disabled, condition, actions, ignoreCount, autoContinue});
|
||||
|
||||
this._id = null;
|
||||
this._sourceCodeLocation = sourceCodeLocation;
|
||||
|
||||
let sourceCode = this._sourceCodeLocation.sourceCode;
|
||||
if (sourceCode) {
|
||||
this._contentIdentifier = sourceCode.contentIdentifier;
|
||||
console.assert(!contentIdentifier || contentIdentifier === this._contentIdentifier, "The content identifier from the source code should match the given value.");
|
||||
} else
|
||||
this._contentIdentifier = contentIdentifier || null;
|
||||
console.assert(this._contentIdentifier || this._isSpecial(), "There should always be a content identifier for a breakpoint.");
|
||||
|
||||
this._scriptIdentifier = sourceCode instanceof WI.Script ? sourceCode.id : null;
|
||||
this._target = sourceCode instanceof WI.Script ? sourceCode.target : null;
|
||||
|
||||
this._resolved = !!resolved;
|
||||
this._resolvedLocations = [];
|
||||
|
||||
this._sourceCodeLocation.addEventListener(WI.SourceCodeLocation.Event.LocationChanged, this._sourceCodeLocationLocationChanged, this);
|
||||
this._sourceCodeLocation.addEventListener(WI.SourceCodeLocation.Event.DisplayLocationChanged, this._sourceCodeLocationDisplayLocationChanged, this);
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static supportsMicrotasks(parameter)
|
||||
{
|
||||
// COMPATIBILITY (iOS 13): Debugger.setPauseOnMicrotasks did not exist yet.
|
||||
return InspectorBackend.hasCommand("Debugger.setPauseOnMicrotasks", parameter);
|
||||
}
|
||||
|
||||
static supportsDebuggerStatements(parameter)
|
||||
{
|
||||
// COMPATIBILITY (iOS 13.1): Debugger.setPauseOnDebuggerStatements did not exist yet.
|
||||
return InspectorBackend.hasCommand("Debugger.setPauseOnDebuggerStatements", parameter);
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static fromJSON(json)
|
||||
{
|
||||
const sourceCode = null;
|
||||
return new WI.JavaScriptBreakpoint(new WI.SourceCodeLocation(sourceCode, json.lineNumber || 0, json.columnNumber || 0), {
|
||||
// The 'url' fallback is for transitioning from older frontends and should be removed.
|
||||
contentIdentifier: json.contentIdentifier || json.url,
|
||||
resolved: json.resolved,
|
||||
disabled: json.disabled,
|
||||
condition: json.condition,
|
||||
actions: json.actions?.map((actionJSON) => WI.BreakpointAction.fromJSON(actionJSON)) || [],
|
||||
ignoreCount: json.ignoreCount,
|
||||
autoContinue: json.autoContinue,
|
||||
});
|
||||
}
|
||||
|
||||
toJSON(key)
|
||||
{
|
||||
// The id, scriptIdentifier, target, and resolved state are tied to the current session, so don't include them for serialization.
|
||||
let json = super.toJSON(key);
|
||||
if (this._contentIdentifier)
|
||||
json.contentIdentifier = this._contentIdentifier;
|
||||
if (isFinite(this._sourceCodeLocation.lineNumber))
|
||||
json.lineNumber = this._sourceCodeLocation.lineNumber;
|
||||
if (isFinite(this._sourceCodeLocation.columnNumber))
|
||||
json.columnNumber = this._sourceCodeLocation.columnNumber;
|
||||
if (key === WI.ObjectStore.toJSONSymbol)
|
||||
json[WI.objectStores.breakpoints.keyPath] = this._contentIdentifier + ":" + this._sourceCodeLocation.lineNumber + ":" + this._sourceCodeLocation.columnNumber;
|
||||
return json;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get sourceCodeLocation() { return this._sourceCodeLocation; }
|
||||
get contentIdentifier() { return this._contentIdentifier; }
|
||||
get scriptIdentifier() { return this._scriptIdentifier; }
|
||||
get target() { return this._target; }
|
||||
get resolvedLocations() { return this._resolvedLocations; }
|
||||
|
||||
get displayName()
|
||||
{
|
||||
switch (this) {
|
||||
case WI.debuggerManager.debuggerStatementsBreakpoint:
|
||||
return WI.repeatedUIString.debuggerStatements();
|
||||
|
||||
case WI.debuggerManager.allExceptionsBreakpoint:
|
||||
return WI.repeatedUIString.allExceptions();
|
||||
|
||||
case WI.debuggerManager.uncaughtExceptionsBreakpoint:
|
||||
return WI.repeatedUIString.uncaughtExceptions();
|
||||
|
||||
case WI.debuggerManager.assertionFailuresBreakpoint:
|
||||
return WI.repeatedUIString.assertionFailures();
|
||||
|
||||
case WI.debuggerManager.allMicrotasksBreakpoint:
|
||||
return WI.repeatedUIString.allMicrotasks();
|
||||
}
|
||||
|
||||
return this._sourceCodeLocation.displayLocationString()
|
||||
}
|
||||
|
||||
get special()
|
||||
{
|
||||
switch (this) {
|
||||
case WI.debuggerManager.debuggerStatementsBreakpoint:
|
||||
case WI.debuggerManager.allExceptionsBreakpoint:
|
||||
case WI.debuggerManager.uncaughtExceptionsBreakpoint:
|
||||
case WI.debuggerManager.assertionFailuresBreakpoint:
|
||||
case WI.debuggerManager.allMicrotasksBreakpoint:
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._isSpecial())
|
||||
return true;
|
||||
|
||||
return super.special;
|
||||
}
|
||||
|
||||
get removable()
|
||||
{
|
||||
switch (this) {
|
||||
case WI.debuggerManager.debuggerStatementsBreakpoint:
|
||||
case WI.debuggerManager.allExceptionsBreakpoint:
|
||||
case WI.debuggerManager.uncaughtExceptionsBreakpoint:
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.removable;
|
||||
}
|
||||
|
||||
get editable()
|
||||
{
|
||||
switch (this) {
|
||||
case WI.debuggerManager.debuggerStatementsBreakpoint:
|
||||
// COMPATIBILITY (iOS 14): Debugger.setPauseOnDebuggerStatements did not have an "options" parameter yet.
|
||||
return WI.JavaScriptBreakpoint.supportsDebuggerStatements("options");
|
||||
|
||||
case WI.debuggerManager.allExceptionsBreakpoint:
|
||||
case WI.debuggerManager.uncaughtExceptionsBreakpoint:
|
||||
// COMPATIBILITY (iOS 14): Debugger.setPauseOnExceptions did not have an "options" parameter yet.
|
||||
return InspectorBackend.hasCommand("Debugger.setPauseOnExceptions", "options");
|
||||
|
||||
case WI.debuggerManager.assertionFailuresBreakpoint:
|
||||
// COMPATIBILITY (iOS 14): Debugger.setPauseOnAssertions did not have an "options" parameter yet.
|
||||
return InspectorBackend.hasCommand("Debugger.setPauseOnAssertions", "options");
|
||||
|
||||
case WI.debuggerManager.allMicrotasksBreakpoint:
|
||||
// COMPATIBILITY (iOS 14): Debugger.setPauseOnMicrotasks did not have an "options" parameter yet.
|
||||
return WI.JavaScriptBreakpoint.supportsMicrotasks("options");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
get identifier()
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
set identifier(id)
|
||||
{
|
||||
this._id = id || null;
|
||||
}
|
||||
|
||||
get resolved()
|
||||
{
|
||||
return super.resolved && this._resolved;
|
||||
}
|
||||
|
||||
set resolved(resolved)
|
||||
{
|
||||
if (this._resolved === resolved)
|
||||
return;
|
||||
|
||||
console.assert(!resolved || this._sourceCodeLocation.sourceCode || this._isSpecial(), "Breakpoints must have a SourceCode to be resolved.", this);
|
||||
|
||||
this._resolved = resolved || false;
|
||||
|
||||
this.dispatchEventToListeners(WI.JavaScriptBreakpoint.Event.ResolvedStateDidChange);
|
||||
}
|
||||
|
||||
addResolvedLocation(location)
|
||||
{
|
||||
console.assert(!this._isSpecial(), this);
|
||||
console.assert(location instanceof WI.SourceCodeLocation, location);
|
||||
console.assert(!this.hasResolvedLocation(location), location, this._resolvedLocations);
|
||||
|
||||
this._resolvedLocations.push(location);
|
||||
this.resolved = true;
|
||||
}
|
||||
|
||||
hasResolvedLocation(location)
|
||||
{
|
||||
console.assert(!this._isSpecial(), this);
|
||||
console.assert(location instanceof WI.SourceCodeLocation, location);
|
||||
|
||||
return this._resolvedLocations.some((resolvedLocation) => resolvedLocation.isEqual(location));
|
||||
}
|
||||
|
||||
clearResolvedLocations()
|
||||
{
|
||||
console.assert(!this._isSpecial(), this);
|
||||
|
||||
this._resolvedLocations = [];
|
||||
this.resolved = false;
|
||||
}
|
||||
|
||||
remove()
|
||||
{
|
||||
super.remove();
|
||||
|
||||
WI.debuggerManager.removeBreakpoint(this);
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie["javascript-breakpoint-content-identifier"] = this._contentIdentifier;
|
||||
cookie["javascript-breakpoint-line-number"] = this._sourceCodeLocation.lineNumber;
|
||||
cookie["javascript-breakpoint-column-number"] = this._sourceCodeLocation.columnNumber;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_isSpecial()
|
||||
{
|
||||
return this._sourceCodeLocation.isEqual(WI.SourceCodeLocation.specialBreakpointLocation);
|
||||
}
|
||||
|
||||
_sourceCodeLocationLocationChanged(event)
|
||||
{
|
||||
this.dispatchEventToListeners(WI.JavaScriptBreakpoint.Event.LocationDidChange, event.data);
|
||||
}
|
||||
|
||||
_sourceCodeLocationDisplayLocationChanged(event)
|
||||
{
|
||||
this.dispatchEventToListeners(WI.JavaScriptBreakpoint.Event.DisplayLocationDidChange, event.data);
|
||||
}
|
||||
};
|
||||
|
||||
WI.JavaScriptBreakpoint.TypeIdentifier = "javascript-breakpoint";
|
||||
|
||||
WI.JavaScriptBreakpoint.Event = {
|
||||
ResolvedStateDidChange: "javascript-breakpoint-resolved-state-did-change",
|
||||
LocationDidChange: "javascript-breakpoint-location-did-change",
|
||||
DisplayLocationDidChange: "javascript-breakpoint-display-location-did-change",
|
||||
};
|
||||
|
||||
WI.JavaScriptBreakpoint.ReferencePage = WI.ReferencePage.JavaScriptBreakpoints;
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.KeyboardShortcut = class KeyboardShortcut
|
||||
{
|
||||
constructor(modifiers, key, callback, targetElement)
|
||||
{
|
||||
console.assert(key);
|
||||
console.assert(!callback || typeof callback === "function");
|
||||
console.assert(!targetElement || targetElement instanceof Element);
|
||||
|
||||
if (typeof key === "string") {
|
||||
key = key[0].toUpperCase();
|
||||
key = new WI.Key(key.charCodeAt(0), key);
|
||||
}
|
||||
|
||||
if (callback && !targetElement)
|
||||
targetElement = document;
|
||||
|
||||
this._modifiers = modifiers || WI.KeyboardShortcut.Modifier.None;
|
||||
this._key = key;
|
||||
this._targetElement = targetElement;
|
||||
this._callback = callback;
|
||||
this._disabled = false;
|
||||
this._implicitlyPreventsDefault = true;
|
||||
|
||||
if (targetElement) {
|
||||
var targetKeyboardShortcuts = targetElement._keyboardShortcuts;
|
||||
if (!targetKeyboardShortcuts)
|
||||
targetKeyboardShortcuts = targetElement._keyboardShortcuts = [];
|
||||
|
||||
targetKeyboardShortcuts.push(this);
|
||||
|
||||
if (!WI.KeyboardShortcut._registeredKeyDownListener) {
|
||||
WI.KeyboardShortcut._registeredKeyDownListener = true;
|
||||
window.addEventListener("keydown", WI.KeyboardShortcut._handleKeyDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static _handleKeyDown(event)
|
||||
{
|
||||
if (event.defaultPrevented)
|
||||
return;
|
||||
|
||||
for (var targetElement = event.target; targetElement; targetElement = targetElement.parentNode) {
|
||||
if (!targetElement._keyboardShortcuts)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < targetElement._keyboardShortcuts.length; ++i) {
|
||||
var keyboardShortcut = targetElement._keyboardShortcuts[i];
|
||||
if (!keyboardShortcut.matchesEvent(event))
|
||||
continue;
|
||||
|
||||
if (!keyboardShortcut.callback)
|
||||
continue;
|
||||
|
||||
keyboardShortcut.callback(event, keyboardShortcut);
|
||||
|
||||
if (keyboardShortcut.implicitlyPreventsDefault)
|
||||
event.preventDefault();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get modifiers()
|
||||
{
|
||||
return this._modifiers;
|
||||
}
|
||||
|
||||
get key()
|
||||
{
|
||||
return this._key;
|
||||
}
|
||||
|
||||
get displayName()
|
||||
{
|
||||
var result = "";
|
||||
|
||||
if (this._modifiers & WI.KeyboardShortcut.Modifier.Control)
|
||||
result += "\u2303";
|
||||
if (this._modifiers & WI.KeyboardShortcut.Modifier.Option)
|
||||
result += WI.Platform.name === "mac" ? "\u2325" : "\u2387";
|
||||
if (this._modifiers & WI.KeyboardShortcut.Modifier.Shift)
|
||||
result += "\u21e7";
|
||||
if (this._modifiers & WI.KeyboardShortcut.Modifier.Command)
|
||||
result += "\u2318";
|
||||
|
||||
result += this._key.toString();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
get callback()
|
||||
{
|
||||
return this._callback;
|
||||
}
|
||||
|
||||
set callback(callback)
|
||||
{
|
||||
console.assert(!callback || typeof callback === "function");
|
||||
|
||||
this._callback = callback || null;
|
||||
}
|
||||
|
||||
get disabled()
|
||||
{
|
||||
return this._disabled;
|
||||
}
|
||||
|
||||
set disabled(disabled)
|
||||
{
|
||||
this._disabled = disabled || false;
|
||||
}
|
||||
|
||||
get implicitlyPreventsDefault()
|
||||
{
|
||||
return this._implicitlyPreventsDefault;
|
||||
}
|
||||
|
||||
set implicitlyPreventsDefault(implicitly)
|
||||
{
|
||||
this._implicitlyPreventsDefault = implicitly;
|
||||
}
|
||||
|
||||
unbind()
|
||||
{
|
||||
this._disabled = true;
|
||||
|
||||
if (!this._targetElement)
|
||||
return;
|
||||
|
||||
var targetKeyboardShortcuts = this._targetElement._keyboardShortcuts;
|
||||
if (!targetKeyboardShortcuts)
|
||||
return;
|
||||
|
||||
targetKeyboardShortcuts.remove(this);
|
||||
}
|
||||
|
||||
matchesEvent(event)
|
||||
{
|
||||
if (this._disabled)
|
||||
return false;
|
||||
|
||||
if (this._key.keyCode !== event.keyCode)
|
||||
return false;
|
||||
|
||||
var eventModifiers = WI.KeyboardShortcut.Modifier.None;
|
||||
if (event.shiftKey)
|
||||
eventModifiers |= WI.KeyboardShortcut.Modifier.Shift;
|
||||
if (event.ctrlKey)
|
||||
eventModifiers |= WI.KeyboardShortcut.Modifier.Control;
|
||||
if (event.altKey)
|
||||
eventModifiers |= WI.KeyboardShortcut.Modifier.Option;
|
||||
if (event.metaKey)
|
||||
eventModifiers |= WI.KeyboardShortcut.Modifier.Command;
|
||||
return this._modifiers === eventModifiers;
|
||||
}
|
||||
};
|
||||
|
||||
WI.Key = class Key
|
||||
{
|
||||
constructor(keyCode, displayName)
|
||||
{
|
||||
this._keyCode = keyCode;
|
||||
this._displayName = displayName;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get keyCode()
|
||||
{
|
||||
return this._keyCode;
|
||||
}
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return this._displayName;
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
return this._displayName;
|
||||
}
|
||||
};
|
||||
|
||||
WI.KeyboardShortcut.Modifier = {
|
||||
None: 0,
|
||||
Shift: 1,
|
||||
Control: 2,
|
||||
Option: 4,
|
||||
Command: 8,
|
||||
|
||||
get CommandOrControl()
|
||||
{
|
||||
return WI.Platform.name === "mac" ? this.Command : this.Control;
|
||||
}
|
||||
};
|
||||
|
||||
WI.KeyboardShortcut.Key = {
|
||||
Backspace: new WI.Key(8, "\u232b"),
|
||||
Tab: new WI.Key(9, "\u21e5"),
|
||||
Enter: new WI.Key(13, "\u21a9"),
|
||||
Escape: new WI.Key(27, "\u238b"),
|
||||
Space: new WI.Key(32, "Space"), // UIString populated in WI.loaded.
|
||||
PageUp: new WI.Key(33, "\u21de"),
|
||||
PageDown: new WI.Key(34, "\u21df"),
|
||||
End: new WI.Key(35, "\u2198"),
|
||||
Home: new WI.Key(36, "\u2196"),
|
||||
Left: new WI.Key(37, "\u2190"),
|
||||
Up: new WI.Key(38, "\u2191"),
|
||||
Right: new WI.Key(39, "\u2192"),
|
||||
Down: new WI.Key(40, "\u2193"),
|
||||
Delete: new WI.Key(46, "\u2326"),
|
||||
Zero: new WI.Key(48, "0"),
|
||||
F1: new WI.Key(112, "F1"),
|
||||
F2: new WI.Key(113, "F2"),
|
||||
F3: new WI.Key(114, "F3"),
|
||||
F4: new WI.Key(115, "F4"),
|
||||
F5: new WI.Key(116, "F5"),
|
||||
F6: new WI.Key(117, "F6"),
|
||||
F7: new WI.Key(118, "F7"),
|
||||
F8: new WI.Key(119, "F8"),
|
||||
F9: new WI.Key(120, "F9"),
|
||||
F10: new WI.Key(121, "F10"),
|
||||
F11: new WI.Key(122, "F11"),
|
||||
F12: new WI.Key(123, "F12"),
|
||||
Semicolon: new WI.Key(186, ";"),
|
||||
Plus: new WI.Key(187, "+"),
|
||||
Comma: new WI.Key(188, ","),
|
||||
Minus: new WI.Key(189, "-"),
|
||||
Period: new WI.Key(190, "."),
|
||||
Slash: new WI.Key(191, "/"),
|
||||
Apostrophe: new WI.Key(192, "`"),
|
||||
LeftCurlyBrace: new WI.Key(219, "{"),
|
||||
Backslash: new WI.Key(220, "\\"),
|
||||
RightCurlyBrace: new WI.Key(221, "}"),
|
||||
SingleQuote: new WI.Key(222, "'")
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Sony Interactive Entertainment Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Layer = class Layer {
|
||||
constructor(layerId, nodeId, bounds, paintCount, memory, compositedBounds, isInShadowTree, isReflection, isGeneratedContent, isAnonymous, pseudoElementId, pseudoElement)
|
||||
{
|
||||
console.assert(typeof bounds === "object");
|
||||
|
||||
this._layerId = layerId;
|
||||
this._nodeId = nodeId;
|
||||
this._bounds = bounds;
|
||||
this._paintCount = paintCount;
|
||||
this._memory = memory;
|
||||
this._compositedBounds = compositedBounds;
|
||||
this._isInShadowTree = isInShadowTree;
|
||||
this._isReflection = isReflection;
|
||||
this._isGeneratedContent = isGeneratedContent;
|
||||
this._isAnonymous = isAnonymous;
|
||||
this._pseudoElementId = pseudoElementId;
|
||||
this._pseudoElement = pseudoElement;
|
||||
|
||||
// Transform compositedBounds to the global position
|
||||
this._compositedBounds.x += this._bounds.x;
|
||||
this._compositedBounds.y += this._bounds.y;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromPayload(payload)
|
||||
{
|
||||
return new WI.Layer(
|
||||
payload.layerId,
|
||||
payload.nodeId,
|
||||
payload.bounds,
|
||||
payload.paintCount,
|
||||
payload.memory,
|
||||
payload.compositedBounds,
|
||||
payload.isInShadowTree,
|
||||
payload.isReflection,
|
||||
payload.isGeneratedContent,
|
||||
payload.isAnonymous,
|
||||
payload.pseudoElementId,
|
||||
payload.pseudoElement
|
||||
);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get layerId() { return this._layerId; }
|
||||
get nodeId() { return this._nodeId; }
|
||||
get bounds() { return this._bounds; }
|
||||
get paintCount() { return this._paintCount; }
|
||||
get memory() { return this._memory; }
|
||||
get compositedBounds() { return this._compositedBounds; }
|
||||
get isInShadowTree() { return this._isInShadowTree; }
|
||||
get isReflection() { return this._isReflection; }
|
||||
get isGeneratedContent() { return this._isGeneratedContent; }
|
||||
get isAnonymous() { return this._isAnonymous; }
|
||||
get pseudoElementId() { return this._pseudoElementId; }
|
||||
get pseudoElement() { return this._pseudoElement; }
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.LayoutInstrument = class LayoutInstrument extends WI.Instrument
|
||||
{
|
||||
// Protected
|
||||
|
||||
get timelineRecordType()
|
||||
{
|
||||
return WI.TimelineRecord.Type.Layout;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.LayoutTimelineRecord = class LayoutTimelineRecord extends WI.TimelineRecord
|
||||
{
|
||||
constructor(eventType, startTime, endTime, stackTrace, sourceCodeLocation, quad)
|
||||
{
|
||||
super(WI.TimelineRecord.Type.Layout, startTime, endTime, stackTrace, sourceCodeLocation);
|
||||
|
||||
console.assert(eventType);
|
||||
console.assert(!quad || quad instanceof WI.Quad);
|
||||
|
||||
if (eventType in WI.LayoutTimelineRecord.EventType)
|
||||
eventType = WI.LayoutTimelineRecord.EventType[eventType];
|
||||
|
||||
this._eventType = eventType;
|
||||
this._quad = quad || null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static displayNameForEventType(eventType)
|
||||
{
|
||||
switch (eventType) {
|
||||
case WI.LayoutTimelineRecord.EventType.InvalidateStyles:
|
||||
return WI.UIString("Styles Invalidated");
|
||||
case WI.LayoutTimelineRecord.EventType.RecalculateStyles:
|
||||
return WI.UIString("Styles Recalculated");
|
||||
case WI.LayoutTimelineRecord.EventType.InvalidateLayout:
|
||||
return WI.UIString("Layout Invalidated");
|
||||
case WI.LayoutTimelineRecord.EventType.ForcedLayout:
|
||||
return WI.UIString("Forced Layout", "Layout phase records that were imperative (forced)");
|
||||
case WI.LayoutTimelineRecord.EventType.Layout:
|
||||
return WI.repeatedUIString.timelineRecordLayout();
|
||||
case WI.LayoutTimelineRecord.EventType.Paint:
|
||||
return WI.repeatedUIString.timelineRecordPaint();
|
||||
case WI.LayoutTimelineRecord.EventType.Composite:
|
||||
return WI.repeatedUIString.timelineRecordComposite();
|
||||
}
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static async fromJSON(json)
|
||||
{
|
||||
let {eventType, startTime, endTime, stackTrace, sourceCodeLocation, quad} = json;
|
||||
quad = quad ? WI.Quad.fromJSON(quad) : null;
|
||||
return new WI.LayoutTimelineRecord(eventType, startTime, endTime, stackTrace, sourceCodeLocation, quad);
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
// FIXME: stackTrace
|
||||
// FIXME: sourceCodeLocation
|
||||
|
||||
return {
|
||||
type: this.type,
|
||||
eventType: this._eventType,
|
||||
startTime: this.startTime,
|
||||
endTime: this.endTime,
|
||||
quad: this._quad || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get eventType()
|
||||
{
|
||||
return this._eventType;
|
||||
}
|
||||
|
||||
get width()
|
||||
{
|
||||
return this._quad ? this._quad.width : NaN;
|
||||
}
|
||||
|
||||
get height()
|
||||
{
|
||||
return this._quad ? this._quad.height : NaN;
|
||||
}
|
||||
|
||||
get area()
|
||||
{
|
||||
return this.width * this.height;
|
||||
}
|
||||
|
||||
get quad()
|
||||
{
|
||||
return this._quad;
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
super.saveIdentityToCookie(cookie);
|
||||
|
||||
cookie[WI.LayoutTimelineRecord.EventTypeCookieKey] = this._eventType;
|
||||
}
|
||||
};
|
||||
|
||||
WI.LayoutTimelineRecord.EventType = {
|
||||
InvalidateStyles: "invalidate-styles",
|
||||
RecalculateStyles: "recalculate-styles",
|
||||
InvalidateLayout: "invalidate-layout",
|
||||
ForcedLayout: "forced-layout",
|
||||
Layout: "layout",
|
||||
Paint: "paint",
|
||||
Composite: "composite"
|
||||
};
|
||||
|
||||
WI.LayoutTimelineRecord.TypeIdentifier = "layout-timeline-record";
|
||||
WI.LayoutTimelineRecord.EventTypeCookieKey = "layout-timeline-record-event-type";
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// FIXME: Investigate folding this into SourceCodeLocation proper so it can always be as lazy as possible.
|
||||
|
||||
// Lazily compute the full SourceCodeLocation information only when such information is needed.
|
||||
// - raw information doesn't require initialization, we have that information
|
||||
// - formatted information does require initialization, done by overriding public APIs.
|
||||
// - display information does require initialization, done by overriding private funnel API resolveMappedLocation.
|
||||
|
||||
WI.LazySourceCodeLocation = class LazySourceCodeLocation extends WI.SourceCodeLocation
|
||||
{
|
||||
constructor(sourceCode, lineNumber, columnNumber)
|
||||
{
|
||||
super(null, lineNumber, columnNumber);
|
||||
|
||||
console.assert(sourceCode);
|
||||
|
||||
this._initialized = false;
|
||||
this._lazySourceCode = sourceCode;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get sourceCode()
|
||||
{
|
||||
return this._lazySourceCode;
|
||||
}
|
||||
|
||||
set sourceCode(sourceCode)
|
||||
{
|
||||
// Getter and setter must be provided together.
|
||||
this.setSourceCode(sourceCode);
|
||||
}
|
||||
|
||||
get formattedLineNumber()
|
||||
{
|
||||
this._lazyInitialization();
|
||||
return this._formattedLineNumber;
|
||||
}
|
||||
|
||||
get formattedColumnNumber()
|
||||
{
|
||||
this._lazyInitialization();
|
||||
return this._formattedColumnNumber;
|
||||
}
|
||||
|
||||
formattedPosition()
|
||||
{
|
||||
this._lazyInitialization();
|
||||
return new WI.SourceCodePosition(this._formattedLineNumber, this._formattedColumnNumber);
|
||||
}
|
||||
|
||||
hasFormattedLocation()
|
||||
{
|
||||
this._lazyInitialization();
|
||||
return super.hasFormattedLocation();
|
||||
}
|
||||
|
||||
hasDifferentDisplayLocation()
|
||||
{
|
||||
this._lazyInitialization();
|
||||
return super.hasDifferentDisplayLocation();
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
resolveMappedLocation()
|
||||
{
|
||||
this._lazyInitialization();
|
||||
super.resolveMappedLocation();
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_lazyInitialization()
|
||||
{
|
||||
if (!this._initialized) {
|
||||
this._initialized = true;
|
||||
this.sourceCode = this._lazySourceCode;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.LineWidget = class LineWidget
|
||||
{
|
||||
constructor(codeMirrorLineWidget, widgetElement)
|
||||
{
|
||||
console.assert(widgetElement instanceof Element);
|
||||
|
||||
this._codeMirrorLineWidget = codeMirrorLineWidget;
|
||||
this._widgetElement = widgetElement;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get codeMirrorLineWidget()
|
||||
{
|
||||
return this._codeMirrorLineWidget;
|
||||
}
|
||||
|
||||
get widgetElement()
|
||||
{
|
||||
return this._widgetElement;
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
this._codeMirrorLineWidget.clear();
|
||||
}
|
||||
|
||||
update()
|
||||
{
|
||||
// FIXME: Later version of CodeMirror has update.
|
||||
if (this._codeMirrorLineWidget.update)
|
||||
this._codeMirrorLineWidget.update();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2019 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NOEVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// LocalResource is a complete resource constructed by the frontend. It
|
||||
// does not need to be tied to an object in the backend. The local resource
|
||||
// does not need to be complete at creation and can get its content later.
|
||||
//
|
||||
// Construction values try to mimic protocol inputs to WI.Resource:
|
||||
//
|
||||
// request: { url, method, headers, timestamp, walltime, finishedTimestamp, data }
|
||||
// response: { mimeType, headers, statusCode, statusText, failureReasonText, content, base64Encoded }
|
||||
// metrics: { responseSource, protocol, priority, remoteAddress, connectionIdentifier, sizes }
|
||||
// timing: { startTime, domainLookupStart, domainLookupEnd, connectStart, connectEnd, secureConnectionStart, requestStart, responseStart, responseEnd }
|
||||
|
||||
WI.LocalResource = class LocalResource extends WI.Resource
|
||||
{
|
||||
constructor({request, response, metrics, timing, mappedFilePath})
|
||||
{
|
||||
console.assert(request);
|
||||
console.assert(typeof request.url === "string");
|
||||
console.assert(response);
|
||||
|
||||
metrics = metrics || {};
|
||||
timing = timing || {};
|
||||
|
||||
super(request.url, {
|
||||
mimeType: response.mimeType || (response.headers || {}).valueForCaseInsensitiveKey("Content-Type") || null,
|
||||
requestMethod: request.method,
|
||||
requestHeaders: request.headers,
|
||||
requestData: request.data,
|
||||
requestSentTimestamp: request.timestamp,
|
||||
requestSentWalltime: request.walltime,
|
||||
});
|
||||
|
||||
// NOTE: This directly overwrites WI.Resource properties.
|
||||
this._finishedOrFailedTimestamp = request.finishedTimestamp || NaN;
|
||||
this._statusCode = response.statusCode || NaN;
|
||||
this._statusText = response.statusText || null;
|
||||
this._responseHeaders = response.headers || {};
|
||||
this._failureReasonText = response.failureReasonText || null;
|
||||
this._timingData = new WI.ResourceTimingData(this, timing);
|
||||
|
||||
this._responseSource = metrics.responseSource || WI.Resource.ResponseSource.Unknown;
|
||||
this._protocol = metrics.protocol || null;
|
||||
this._priority = metrics.priority || WI.Resource.NetworkPriority.Unknown;
|
||||
this._remoteAddress = metrics.remoteAddress || null;
|
||||
this._connectionIdentifier = metrics.connectionIdentifier || null;
|
||||
this._requestHeadersTransferSize = !isNaN(metrics.requestHeaderBytesSent) ? metrics.requestHeaderBytesSent : NaN;
|
||||
this._requestBodyTransferSize = !isNaN(metrics.requestBodyBytesSent) ? metrics.requestBodyBytesSent : NaN;
|
||||
this._responseHeadersTransferSize = !isNaN(metrics.responseHeaderBytesReceived) ? metrics.responseHeaderBytesReceived : NaN;
|
||||
this._responseBodyTransferSize = !isNaN(metrics.responseBodyBytesReceived) ? metrics.responseBodyBytesReceived : NaN;
|
||||
this._responseBodySize = !isNaN(metrics.responseBodyDecodedSize) ? metrics.responseBodyDecodedSize : NaN;
|
||||
this._isProxyConnection = !!metrics.isProxyConnection;
|
||||
|
||||
// Set by `WI.LocalResourceOverride`.
|
||||
this._localResourceOverride = null;
|
||||
|
||||
// Finalize WI.Resource.
|
||||
this._finished = true;
|
||||
this._failed = false; // FIXME: How should we denote a failure? Assume from status code / failure reason?
|
||||
this._cached = false; // FIXME: How should we denote cached? Assume from response source?
|
||||
|
||||
// Finalize WI.SourceCode.
|
||||
let content = response.content || "";
|
||||
let base64Encoded = response.base64Encoded || false;
|
||||
this._originalRevision = new WI.SourceCodeRevision(this, content, base64Encoded, this._mimeType);
|
||||
this._currentRevision = this._originalRevision;
|
||||
|
||||
this._mappedFilePath = mappedFilePath || null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static canMapToFile()
|
||||
{
|
||||
return InspectorFrontendHost.canLoad();
|
||||
}
|
||||
|
||||
static headersArrayToHeadersObject(headers)
|
||||
{
|
||||
let result = {};
|
||||
if (headers) {
|
||||
for (let {name, value} of headers)
|
||||
result[name] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static fromHAREntry(entry, archiveStartWalltime)
|
||||
{
|
||||
// FIXME: <https://webkit.org/b/195694> Web Inspector: HAR Extension for Redirect Timing Info
|
||||
|
||||
let {request, response, startedDateTime, timings} = entry;
|
||||
let requestSentWalltime = WI.HARBuilder.dateFromHARDate(startedDateTime) / 1000;
|
||||
let requestSentTimestamp = requestSentWalltime - archiveStartWalltime;
|
||||
let finishedTimestamp = NaN;
|
||||
|
||||
let timing = {
|
||||
startTime: NaN,
|
||||
domainLookupStart: NaN,
|
||||
domainLookupEnd: NaN,
|
||||
connectStart: NaN,
|
||||
connectEnd: NaN,
|
||||
secureConnectionStart: NaN,
|
||||
requestStart: NaN,
|
||||
responseStart: NaN,
|
||||
responseEnd: NaN,
|
||||
};
|
||||
|
||||
if (!isNaN(requestSentWalltime) && !isNaN(archiveStartWalltime)) {
|
||||
let hasBlocked = timings.blocked !== -1;
|
||||
let hasDNS = timings.dns !== -1;
|
||||
let hasConnect = timings.connect !== -1;
|
||||
let hasSecureConnect = timings.ssl !== -1;
|
||||
|
||||
// FIXME: <https://webkit.org/b/195692> Web Inspector: ResourceTimingData should allow a startTime of 0
|
||||
timing.startTime = requestSentTimestamp || Number.EPSILON;
|
||||
timing.fetchStart = timing.startTime;
|
||||
|
||||
let accumulation = timing.startTime;
|
||||
|
||||
if (hasBlocked)
|
||||
accumulation += (timings.blocked / 1000);
|
||||
|
||||
if (hasDNS) {
|
||||
timing.domainLookupStart = accumulation;
|
||||
accumulation += (timings.dns / 1000);
|
||||
timing.domainLookupEnd = accumulation;
|
||||
}
|
||||
|
||||
if (hasConnect) {
|
||||
timing.connectStart = accumulation;
|
||||
accumulation += (timings.connect / 1000);
|
||||
timing.connectEnd = accumulation;
|
||||
|
||||
if (hasSecureConnect)
|
||||
timing.secureConnectionStart = timing.connectEnd - (timings.ssl / 1000);
|
||||
}
|
||||
|
||||
accumulation += (timings.send / 1000);
|
||||
timing.requestStart = accumulation;
|
||||
|
||||
accumulation += (timings.wait / 1000);
|
||||
timing.responseStart = accumulation;
|
||||
|
||||
accumulation += (timings.receive / 1000);
|
||||
timing.responseEnd = accumulation;
|
||||
|
||||
finishedTimestamp = timing.responseEnd;
|
||||
}
|
||||
|
||||
let serverAddress = entry.serverIPAddress || null;
|
||||
if (serverAddress && typeof entry._serverPort === "number")
|
||||
serverAddress += ":" + entry._serverPort;
|
||||
|
||||
return new WI.LocalResource({
|
||||
request: {
|
||||
url: request.url,
|
||||
method: request.method,
|
||||
headers: LocalResource.headersArrayToHeadersObject(request.headers),
|
||||
timestamp: requestSentTimestamp,
|
||||
walltime: requestSentWalltime,
|
||||
finishedTimestamp: finishedTimestamp,
|
||||
data: request.postData ? request.postData.text : null,
|
||||
},
|
||||
response: {
|
||||
headers: LocalResource.headersArrayToHeadersObject(response.headers),
|
||||
mimeType: response.content.mimeType,
|
||||
statusCode: response.status,
|
||||
statusText: response.statusText,
|
||||
failureReasonText: response._error || null,
|
||||
content: response.content.text,
|
||||
base64Encoded: response.content.encoding === "base64",
|
||||
},
|
||||
metrics: {
|
||||
responseSource: WI.HARBuilder.responseSourceFromHARFetchType(entry._fetchType),
|
||||
protocol: WI.HARBuilder.protocolFromHARProtocol(response.httpVersion),
|
||||
priority: WI.HARBuilder.networkPriorityFromHARPriority(entry._priority),
|
||||
remoteAddress: serverAddress,
|
||||
connectionIdentifier: entry.connection ? parseInt(entry.connection) : null,
|
||||
requestHeaderBytesSent: request.headersSize >= 0 ? request.headersSize : NaN,
|
||||
requestBodyBytesSent: request.bodySize >= 0 ? request.bodySize : NaN,
|
||||
responseHeaderBytesReceived: response.headersSize >= 0 ? response.headersSize : NaN,
|
||||
responseBodyBytesReceived: response.bodySize >= 0 ? response.bodySize : NaN,
|
||||
responseBodyDecodedSize: response.content.size || NaN,
|
||||
},
|
||||
timing,
|
||||
});
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static fromJSON(json)
|
||||
{
|
||||
return new WI.LocalResource(json);
|
||||
}
|
||||
|
||||
toJSON(key)
|
||||
{
|
||||
return {
|
||||
request: {
|
||||
url: this.url,
|
||||
method: this.requestMethod,
|
||||
headers: this.requestHeaders,
|
||||
data: this.requestData,
|
||||
},
|
||||
response: {
|
||||
headers: this.responseHeaders,
|
||||
mimeType: this.mimeType,
|
||||
statusCode: this.statusCode,
|
||||
statusText: this.statusText,
|
||||
content: this.currentRevision.content,
|
||||
base64Encoded: this.currentRevision.base64Encoded,
|
||||
},
|
||||
mappedFilePath: this._mappedFilePath,
|
||||
};
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get localResourceOverride() { return this._localResourceOverride; }
|
||||
|
||||
get mappedFilePath()
|
||||
{
|
||||
return this._mappedFilePath;
|
||||
}
|
||||
|
||||
set mappedFilePath(mappedFilePath)
|
||||
{
|
||||
console.assert(WI.LocalResource.canMapToFile());
|
||||
console.assert(mappedFilePath);
|
||||
|
||||
if (mappedFilePath === this._mappedFilePath)
|
||||
return;
|
||||
|
||||
this._mappedFilePath = mappedFilePath;
|
||||
|
||||
const forceUpdate = true;
|
||||
this._loadFromFileSystem(forceUpdate).then(() => {
|
||||
this.dispatchEventToListeners(WI.LocalResource.Event.MappedFilePathChanged);
|
||||
});
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
async requestContent()
|
||||
{
|
||||
await this._loadFromFileSystem();
|
||||
|
||||
return super.requestContent();
|
||||
}
|
||||
|
||||
requestContentFromBackend()
|
||||
{
|
||||
return Promise.resolve({
|
||||
content: this._originalRevision.content,
|
||||
base64Encoded: this._originalRevision.base64Encoded,
|
||||
});
|
||||
}
|
||||
|
||||
handleCurrentRevisionContentChange()
|
||||
{
|
||||
if (this._mimeType !== this.currentRevision.mimeType) {
|
||||
let oldMIMEType = this._mimeType;
|
||||
this._mimeType = this.currentRevision.mimeType;
|
||||
this.dispatchEventToListeners(WI.Resource.Event.MIMETypeDidChange, {oldMIMEType});
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
async _loadFromFileSystem(forceUpdate)
|
||||
{
|
||||
if (!this._mappedFilePath)
|
||||
return;
|
||||
|
||||
let content = "";
|
||||
try {
|
||||
content = await InspectorFrontendHost.load(this._mappedFilePath);
|
||||
} catch {
|
||||
if (this._didWarnAboutFailureToLoadFromFileSystem)
|
||||
return;
|
||||
|
||||
this._didWarnAboutFailureToLoadFromFileSystem = true;
|
||||
setTimeout(() => {
|
||||
this._didWarnAboutFailureToLoadFromFileSystem = false;
|
||||
});
|
||||
|
||||
let message = WI.UIString("Local Override: could not load \u201C%s\u201D").format(this._mappedFilePath);
|
||||
|
||||
if (window.InspectorTest) {
|
||||
console.warn(message);
|
||||
return;
|
||||
}
|
||||
|
||||
let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Warning, message);
|
||||
consoleMessage.shouldRevealConsole = true;
|
||||
|
||||
WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!forceUpdate && content === this.currentRevision.content)
|
||||
return;
|
||||
|
||||
this.editableRevision.updateRevisionContent(content);
|
||||
}
|
||||
};
|
||||
|
||||
WI.LocalResource.Event = {
|
||||
MappedFilePathChanged: "local-resource-mapped-file-path-changed",
|
||||
};
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.LocalResourceOverride = class LocalResourceOverride extends WI.Object
|
||||
{
|
||||
constructor(url, type, localResource, {resourceErrorType, isCaseSensitive, isRegex, isPassthrough, disabled} = {})
|
||||
{
|
||||
console.assert(url && typeof url === "string", url);
|
||||
console.assert(Object.values(WI.LocalResourceOverride.InterceptType).includes(type), type);
|
||||
console.assert(localResource instanceof WI.LocalResource, localResource);
|
||||
console.assert(!localResource.localResourceOverride, localResource);
|
||||
console.assert(!resourceErrorType || Object.values(WI.LocalResourceOverride.ResourceErrorType).includes(resourceErrorType), resourceErrorType);
|
||||
console.assert(isCaseSensitive === undefined || typeof isCaseSensitive === "boolean", isCaseSensitive);
|
||||
console.assert(isRegex === undefined || typeof isRegex === "boolean", isRegex);
|
||||
console.assert(isPassthrough === undefined || typeof isPassthrough === "boolean", isPassthrough);
|
||||
console.assert(disabled === undefined || typeof disabled === "boolean", disabled);
|
||||
|
||||
super();
|
||||
|
||||
this._url = WI.urlWithoutFragment(url);
|
||||
this._urlComponents = null;
|
||||
this._type = type;
|
||||
this._localResource = localResource;
|
||||
this._resourceErrorType = resourceErrorType || WI.LocalResourceOverride.ResourceErrorType.General;
|
||||
this._isCaseSensitive = isCaseSensitive !== undefined ? isCaseSensitive : true;
|
||||
this._isRegex = isRegex !== undefined ? isRegex : false;
|
||||
this._isPassthrough = isPassthrough !== undefined ? isPassthrough : false;
|
||||
this._disabled = disabled !== undefined ? disabled : false;
|
||||
|
||||
this._localResource._localResourceOverride = this;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static create(url, type, {requestURL, requestMethod, requestHeaders, requestData, responseMIMEType, responseContent, responseBase64Encoded, responseStatusCode, responseStatusText, responseHeaders, resourceErrorType, isCaseSensitive, isRegex, isPassthrough, disabled} = {})
|
||||
{
|
||||
let localResource = new WI.LocalResource({
|
||||
request: {
|
||||
url: requestURL || url || "",
|
||||
method: requestMethod,
|
||||
headers: requestHeaders,
|
||||
data: requestData,
|
||||
},
|
||||
response: {
|
||||
headers: responseHeaders,
|
||||
mimeType: responseMIMEType,
|
||||
statusCode: responseStatusCode,
|
||||
statusText: responseStatusText,
|
||||
content: responseContent,
|
||||
base64Encoded: responseBase64Encoded,
|
||||
},
|
||||
});
|
||||
return new WI.LocalResourceOverride(url, type, localResource, {resourceErrorType, isCaseSensitive, isRegex, isPassthrough, disabled});
|
||||
}
|
||||
|
||||
static displayNameForNetworkStageOfType(type)
|
||||
{
|
||||
switch (type) {
|
||||
case WI.LocalResourceOverride.InterceptType.Block:
|
||||
case WI.LocalResourceOverride.InterceptType.Request:
|
||||
return WI.UIString("Request Override", "Request Override @ Local Override Network Stage", "Text indicating that the local override replaces the request of the network activity.");
|
||||
|
||||
case WI.LocalResourceOverride.InterceptType.Response:
|
||||
case WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork:
|
||||
return WI.UIString("Response Override", "Response Override @ Local Override Network Stage", "Text indicating that the local override replaces the response of the network activity.");
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown type: ", type);
|
||||
return "";
|
||||
}
|
||||
|
||||
static displayNameForType(type)
|
||||
{
|
||||
switch (type) {
|
||||
case WI.LocalResourceOverride.InterceptType.Block:
|
||||
return WI.UIString("Block", "Block @ Local Override Type", "Text indicating that the local override will always block the network activity.");
|
||||
|
||||
case WI.LocalResourceOverride.InterceptType.Request:
|
||||
return WI.UIString("Request", "Request @ Local Override Type", "Text indicating that the local override intercepts the request phase of network activity.");
|
||||
|
||||
case WI.LocalResourceOverride.InterceptType.Response:
|
||||
return WI.UIString("Response", "Response @ Local Override Type", "Text indicating that the local override intercepts the response phase of network activity.");
|
||||
|
||||
case WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork:
|
||||
return WI.UIString("Response (skip network)", "Response (skip network) @ Local Override Type", "Text indicating that the local override will skip all network activity and instead immediately serve the response.");
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown type: ", type);
|
||||
return "";
|
||||
}
|
||||
|
||||
static displayNameForResourceErrorType(resourceErrorType)
|
||||
{
|
||||
switch (resourceErrorType) {
|
||||
case WI.LocalResourceOverride.ResourceErrorType.AccessControl:
|
||||
return WI.UIString("Access Control", "Access Control @ Local Override Type", "Text indicating that the local override will block the network activity with an access error.");
|
||||
|
||||
case WI.LocalResourceOverride.ResourceErrorType.Cancellation:
|
||||
return WI.UIString("Cancellation", "Cancellation @ Local Override Type", "Text indicating that the local override will block the network activity with a cancellation error.");
|
||||
|
||||
case WI.LocalResourceOverride.ResourceErrorType.General:
|
||||
return WI.UIString("General", "General @ Local Override Type", "Text indicating that the local override will block the network activity with a general error.");
|
||||
|
||||
case WI.LocalResourceOverride.ResourceErrorType.Timeout:
|
||||
return WI.UIString("Timeout", "Timeout @ Local Override Type", "Text indicating that the local override will block the network activity with an timeout error.");
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown resource error type: ", resourceErrorType);
|
||||
return "";
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static fromJSON(json)
|
||||
{
|
||||
let {url, type, localResource: localResourceJSON, resourceErrorType, isCaseSensitive, isRegex, isPassthrough, disabled} = json;
|
||||
|
||||
let localResource = WI.LocalResource.fromJSON(localResourceJSON);
|
||||
|
||||
// COMPATIBILITY (iOS 13.4): Network.interceptWithRequest/Network.interceptRequestWithResponse did not exist yet.
|
||||
url ??= localResource.url;
|
||||
type ??= WI.LocalResourceOverride.InterceptType.Response;
|
||||
|
||||
return new WI.LocalResourceOverride(url, type, localResource, {resourceErrorType, isCaseSensitive, isRegex, isPassthrough, disabled});
|
||||
}
|
||||
|
||||
toJSON(key)
|
||||
{
|
||||
let json = {
|
||||
url: this._url,
|
||||
type: this._type,
|
||||
localResource: this._localResource.toJSON(key),
|
||||
isCaseSensitive: this._isCaseSensitive,
|
||||
isRegex: this._isRegex,
|
||||
isPassthrough: this._isPassthrough,
|
||||
disabled: this._disabled,
|
||||
};
|
||||
|
||||
if (this._resourceErrorType)
|
||||
json.resourceErrorType = this._resourceErrorType;
|
||||
|
||||
if (key === WI.ObjectStore.toJSONSymbol)
|
||||
json[WI.objectStores.localResourceOverrides.keyPath] = this._url;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get url() { return this._url; }
|
||||
get type() { return this._type; }
|
||||
get localResource() { return this._localResource; }
|
||||
get isCaseSensitive() { return this._isCaseSensitive; }
|
||||
get isRegex() { return this._isRegex; }
|
||||
get isPassthrough() { return this._isPassthrough; }
|
||||
|
||||
get urlComponents()
|
||||
{
|
||||
if (!this._urlComponents)
|
||||
this._urlComponents = parseURL(this._url);
|
||||
return this._urlComponents;
|
||||
}
|
||||
|
||||
get resourceErrorType()
|
||||
{
|
||||
return this._resourceErrorType;
|
||||
}
|
||||
|
||||
set resourceErrorType(resourceErrorType)
|
||||
{
|
||||
console.assert(Object.values(WI.LocalResourceOverride.ResourceErrorType).includes(resourceErrorType), resourceErrorType);
|
||||
|
||||
if (this._resourceErrorType === resourceErrorType)
|
||||
return;
|
||||
|
||||
this._resourceErrorType = resourceErrorType;
|
||||
|
||||
this.dispatchEventToListeners(WI.LocalResourceOverride.Event.ResourceErrorTypeChanged);
|
||||
}
|
||||
|
||||
get disabled()
|
||||
{
|
||||
return this._disabled;
|
||||
}
|
||||
|
||||
set disabled(disabled)
|
||||
{
|
||||
if (this._disabled === disabled)
|
||||
return;
|
||||
|
||||
this._disabled = !!disabled;
|
||||
|
||||
this.dispatchEventToListeners(WI.LocalResourceOverride.Event.DisabledChanged);
|
||||
}
|
||||
|
||||
get displayName()
|
||||
{
|
||||
return this.displayURL();
|
||||
}
|
||||
|
||||
displayURL({full} = {})
|
||||
{
|
||||
if (this._isRegex)
|
||||
return "/" + this._url + "/" + (!this._isCaseSensitive ? "i" : "");
|
||||
|
||||
let displayName = full ? this._url : WI.displayNameForURL(this._url, this.urlComponents);
|
||||
if (!this._isCaseSensitive)
|
||||
displayName = WI.UIString("%s (Case Insensitive)", "%s (Case Insensitive) @ Local Override", "Label for case-insensitive URL match pattern of a local override.").format(displayName);
|
||||
return displayName;
|
||||
}
|
||||
|
||||
generateRequestRedirectURL(url)
|
||||
{
|
||||
console.assert(this._type === WI.LocalResourceOverride.InterceptType.Request, this);
|
||||
|
||||
let redirectURL = this._localResource.url;
|
||||
if (!redirectURL)
|
||||
return null;
|
||||
|
||||
if (this._isRegex)
|
||||
return url.replace(this._urlRegex, redirectURL);
|
||||
|
||||
return redirectURL;
|
||||
}
|
||||
|
||||
get canMapToFile()
|
||||
{
|
||||
if (!WI.LocalResource.canMapToFile())
|
||||
return false;
|
||||
|
||||
switch (this._type) {
|
||||
case WI.LocalResourceOverride.InterceptType.Block:
|
||||
case WI.LocalResourceOverride.InterceptType.Request:
|
||||
return false;
|
||||
|
||||
case WI.LocalResourceOverride.InterceptType.Response:
|
||||
case WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork:
|
||||
return true;
|
||||
}
|
||||
|
||||
console.assert(false, "not reached");
|
||||
return false;
|
||||
}
|
||||
|
||||
matches(url)
|
||||
{
|
||||
url = WI.urlWithoutFragment(url);
|
||||
|
||||
if (this._isRegex)
|
||||
return this._urlRegex.test(url);
|
||||
|
||||
if (!this._isCaseSensitive)
|
||||
return url.toLowerCase() === this._url.toLowerCase();
|
||||
|
||||
return url === this._url;
|
||||
}
|
||||
|
||||
equals(localResourceOverrideOrSerializedData)
|
||||
{
|
||||
console.assert(localResourceOverrideOrSerializedData instanceof WI.LocalResourceOverride || (localResourceOverrideOrSerializedData && typeof localResourceOverrideOrSerializedData === "object"), localResourceOverrideOrSerializedData);
|
||||
|
||||
return localResourceOverrideOrSerializedData.url === this._url
|
||||
&& localResourceOverrideOrSerializedData.isCaseSensitive === this._isCaseSensitive
|
||||
&& localResourceOverrideOrSerializedData.isRegex === this._isRegex;
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
cookie["local-resource-override-url"] = this._url;
|
||||
cookie["local-resource-override-type"] = this._type;
|
||||
cookie["local-resource-override-is-case-sensitive"] = this._isCaseSensitive;
|
||||
cookie["local-resource-override-is-regex"] = this._isRegex;
|
||||
cookie["local-resource-override-disabled"] = this._disabled;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
get _urlRegex()
|
||||
{
|
||||
console.assert(this._isRegex);
|
||||
return new RegExp(this._url, !this._isCaseSensitive ? "i" : "");
|
||||
}
|
||||
};
|
||||
|
||||
WI.LocalResourceOverride.TypeIdentifier = "local-resource-override";
|
||||
|
||||
WI.LocalResourceOverride.InterceptType = {
|
||||
Block: "block",
|
||||
Request: "request",
|
||||
Response: "response",
|
||||
ResponseSkippingNetwork: "response-skipping-network",
|
||||
};
|
||||
|
||||
WI.LocalResourceOverride.ResourceErrorType = {
|
||||
AccessControl: "AccessControl",
|
||||
Cancellation: "Cancellation",
|
||||
General: "General",
|
||||
Timeout: "Timeout",
|
||||
};
|
||||
|
||||
WI.LocalResourceOverride.Event = {
|
||||
DisabledChanged: "local-resource-override-disabled-state-did-change",
|
||||
ResourceErrorTypeChanged: "local-resource-override-resource-error-type-changed",
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.LocalScript = class LocalScript extends WI.Script
|
||||
{
|
||||
constructor(target, url, sourceURL, sourceType, source, {injected, editable} = {})
|
||||
{
|
||||
const id = null;
|
||||
super(target, id, WI.TextRange.fromText(source), url, sourceType, injected, sourceURL);
|
||||
|
||||
this._editable = !!editable;
|
||||
|
||||
// Finalize WI.SourceCode.
|
||||
const base64Encoded = false;
|
||||
const mimeType = "text/javascript";
|
||||
this._originalRevision = new WI.SourceCodeRevision(this, source, base64Encoded, mimeType);
|
||||
this._currentRevision = this._originalRevision;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get editable() { return this._editable; }
|
||||
|
||||
get supportsScriptBlackboxing()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
requestContentFromBackend()
|
||||
{
|
||||
return Promise.resolve({
|
||||
scriptSource: this._originalRevision.content,
|
||||
});
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
handleCurrentRevisionContentChange()
|
||||
{
|
||||
super.handleCurrentRevisionContentChange();
|
||||
|
||||
this._range = WI.TextRange.fromText(this._currentRevision.content);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.LogObject = class LogObject
|
||||
{
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.LoggingChannel = class LoggingChannel
|
||||
{
|
||||
constructor(source, level)
|
||||
{
|
||||
console.assert(typeof source === "string");
|
||||
console.assert(Object.values(WI.ConsoleMessage.MessageSource).includes(source), source);
|
||||
|
||||
console.assert(typeof level === "string");
|
||||
console.assert(Object.values(WI.LoggingChannel.Level).includes(level), level);
|
||||
|
||||
this._source = source;
|
||||
this._level = level;
|
||||
}
|
||||
|
||||
// Payload
|
||||
|
||||
static fromPayload(payload)
|
||||
{
|
||||
return new WI.LoggingChannel(payload.source, payload.level);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get source() { return this._source; }
|
||||
get level() { return this._level; }
|
||||
};
|
||||
|
||||
WI.LoggingChannel.Level = {
|
||||
Off: "off",
|
||||
Basic: "basic",
|
||||
Verbose: "verbose",
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.MediaInstrument = class MediaInstrument extends WI.Instrument
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(WI.MediaInstrument.supported());
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static supported()
|
||||
{
|
||||
// COMPATIBILITY (iOS 12): DOM.didFireEvent did not exist yet.
|
||||
return InspectorBackend.hasEvent("DOM.didFireEvent");
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
get timelineRecordType()
|
||||
{
|
||||
return WI.TimelineRecord.Type.Media;
|
||||
}
|
||||
|
||||
startInstrumentation(initiatedByBackend)
|
||||
{
|
||||
// Audio/Video/Picture instrumentation is always happening.
|
||||
|
||||
if (!initiatedByBackend) {
|
||||
// COMPATIBILITY (iOS 13): Animation domain did not exist yet.
|
||||
if (InspectorBackend.hasDomain("Animation")) {
|
||||
let target = WI.assumingMainTarget();
|
||||
target.AnimationAgent.startTracking();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopInstrumentation(initiatedByBackend)
|
||||
{
|
||||
// Audio/Video/Picture instrumentation is always happening.
|
||||
|
||||
if (!initiatedByBackend) {
|
||||
// COMPATIBILITY (iOS 13): Animation domain did not exist yet.
|
||||
if (InspectorBackend.hasDomain("Animation")) {
|
||||
let target = WI.assumingMainTarget();
|
||||
target.AnimationAgent.stopTracking();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.MediaTimeline = class MediaTimeline extends WI.Timeline
|
||||
{
|
||||
// Public
|
||||
|
||||
recordForTrackingAnimationId(trackingAnimationId)
|
||||
{
|
||||
return this._trackingAnimationIdRecordMap.get(trackingAnimationId) || null;
|
||||
}
|
||||
|
||||
recordForMediaElementEvents(domNode)
|
||||
{
|
||||
console.assert(domNode.isMediaElement());
|
||||
return this._mediaElementRecordMap.get(domNode) || null;
|
||||
}
|
||||
|
||||
reset(suppressEvents)
|
||||
{
|
||||
this._trackingAnimationIdRecordMap = new Map;
|
||||
this._mediaElementRecordMap = new Map;
|
||||
|
||||
super.reset(suppressEvents);
|
||||
}
|
||||
|
||||
addRecord(record, options = {})
|
||||
{
|
||||
console.assert(record instanceof WI.MediaTimelineRecord);
|
||||
|
||||
if (record.trackingAnimationId) {
|
||||
console.assert(!this._trackingAnimationIdRecordMap.has(record.trackingAnimationId));
|
||||
this._trackingAnimationIdRecordMap.set(record.trackingAnimationId, record);
|
||||
}
|
||||
|
||||
if (record.eventType === WI.MediaTimelineRecord.EventType.MediaElement) {
|
||||
console.assert(!(record.domNode instanceof WI.DOMNode) || record.domNode.isMediaElement());
|
||||
console.assert(!this._mediaElementRecordMap.has(record.domNode));
|
||||
this._mediaElementRecordMap.set(record.domNode, record);
|
||||
}
|
||||
|
||||
super.addRecord(record, options);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.MediaTimelineRecord = class MediaTimelineRecord extends WI.TimelineRecord
|
||||
{
|
||||
constructor(eventType, domNodeOrInfo, {trackingAnimationId, animationName, transitionProperty} = {})
|
||||
{
|
||||
console.assert(Object.values(MediaTimelineRecord.EventType).includes(eventType));
|
||||
console.assert(domNodeOrInfo instanceof WI.DOMNode || (!isEmptyObject(domNodeOrInfo) && domNodeOrInfo.displayName && domNodeOrInfo.cssPath));
|
||||
|
||||
super(WI.TimelineRecord.Type.Media);
|
||||
|
||||
this._eventType = eventType;
|
||||
this._domNode = domNodeOrInfo;
|
||||
this._domNodeDisplayName = domNodeOrInfo?.displayName;
|
||||
this._domNodeCSSPath = domNodeOrInfo instanceof WI.DOMNode ? WI.cssPath(domNodeOrInfo, {full: true}) : domNodeOrInfo?.cssPath;
|
||||
|
||||
// Web Animation
|
||||
console.assert(trackingAnimationId === undefined || typeof trackingAnimationId === "string");
|
||||
this._trackingAnimationId = trackingAnimationId || null;
|
||||
|
||||
// CSS Web Animation
|
||||
console.assert(animationName === undefined || typeof animationName === "string");
|
||||
console.assert(transitionProperty === undefined || typeof transitionProperty === "string");
|
||||
this._animationName = animationName || null;
|
||||
this._transitionProperty = transitionProperty || null;
|
||||
|
||||
this._timestamps = [];
|
||||
this._activeStartTime = NaN;
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static async fromJSON(json)
|
||||
{
|
||||
let {eventType, domNodeDisplayName, domNodeCSSPath, animationName, transitionProperty, timestamps} = json;
|
||||
|
||||
let documentNode = null;
|
||||
if (InspectorBackend.hasDomain("DOM"))
|
||||
documentNode = await new Promise((resolve) => WI.domManager.requestDocument(resolve));
|
||||
|
||||
let domNode = null;
|
||||
if (documentNode && domNodeCSSPath) {
|
||||
try {
|
||||
let nodeId = await documentNode.querySelector(domNodeCSSPath);
|
||||
if (nodeId)
|
||||
domNode = WI.domManager.nodeForId(nodeId);
|
||||
} catch { }
|
||||
}
|
||||
if (!domNode) {
|
||||
domNode = {
|
||||
displayName: domNodeDisplayName,
|
||||
cssPath: domNodeCSSPath,
|
||||
};
|
||||
}
|
||||
|
||||
let record = new MediaTimelineRecord(eventType, domNode, {animationName, transitionProperty});
|
||||
|
||||
if (Array.isArray(timestamps) && timestamps.length) {
|
||||
record._timestamps = [];
|
||||
for (let item of timestamps) {
|
||||
if (item.type === MediaTimelineRecord.TimestampType.MediaElementDOMEvent) {
|
||||
if (documentNode && item.originatorCSSPath) {
|
||||
try {
|
||||
let nodeId = await documentNode.querySelector(item.originatorCSSPath);
|
||||
if (nodeId)
|
||||
item.originator = WI.domManager.nodeForId(nodeId);
|
||||
} catch { }
|
||||
if (!item.originator) {
|
||||
item.originator = {
|
||||
displayName: item.originatorDisplayName,
|
||||
cssPath: item.originatorCSSPath,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
record._timestamps.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
let json = {
|
||||
eventType: this._eventType,
|
||||
domNodeDisplayName: this._domNodeDisplayName,
|
||||
domNodeCSSPath: this._domNodeCSSPath,
|
||||
};
|
||||
|
||||
if (this._animationName)
|
||||
json.animationName = this._animationName;
|
||||
|
||||
if (this._transitionProperty)
|
||||
json.transitionProperty = this._transitionProperty;
|
||||
|
||||
if (this._timestamps.length) {
|
||||
json.timestamps = this._timestamps.map((item) => {
|
||||
if (item.type === MediaTimelineRecord.TimestampType.MediaElementDOMEvent && item.originator instanceof WI.DOMNode)
|
||||
delete item.originator;
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get eventType() { return this._eventType; }
|
||||
get domNode() { return this._domNode; }
|
||||
get trackingAnimationId() { return this._trackingAnimationId; }
|
||||
get timestamps() { return this._timestamps; }
|
||||
get activeStartTime() { return this._activeStartTime; }
|
||||
|
||||
get updatesDynamically()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get usesActiveStartTime()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get displayName()
|
||||
{
|
||||
switch (this._eventType) {
|
||||
case MediaTimelineRecord.EventType.CSSAnimation:
|
||||
return this._animationName;
|
||||
|
||||
case MediaTimelineRecord.EventType.CSSTransition:
|
||||
return this._transitionProperty;
|
||||
|
||||
case MediaTimelineRecord.EventType.MediaElement:
|
||||
return WI.UIString("Media Element");
|
||||
}
|
||||
|
||||
console.error("Unknown media record event type: ", this._eventType, this);
|
||||
return WI.UIString("Media Event");
|
||||
}
|
||||
|
||||
get subtitle()
|
||||
{
|
||||
switch (this._eventType) {
|
||||
case MediaTimelineRecord.EventType.CSSAnimation:
|
||||
return WI.UIString("CSS Animation");
|
||||
|
||||
case MediaTimelineRecord.EventType.CSSTransition:
|
||||
return WI.UIString("CSS Transition");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
saveIdentityToCookie(cookie)
|
||||
{
|
||||
super.saveIdentityToCookie(cookie);
|
||||
|
||||
cookie["media-timeline-record-event-type"] = this._eventType;
|
||||
cookie["media-timeline-record-dom-node"] = this._domNode instanceof WI.DOMNode ? this._domNode.path() : this._domNode;
|
||||
if (this._animationName)
|
||||
cookie["media-timeline-record-animation-name"] = this._animationName;
|
||||
if (this._transitionProperty)
|
||||
cookie["media-timeline-record-transition-property"] = this._transitionProperty;
|
||||
}
|
||||
|
||||
// TimelineManager
|
||||
|
||||
updateAnimationState(timestamp, animationState)
|
||||
{
|
||||
console.assert(this._eventType === MediaTimelineRecord.EventType.CSSAnimation || this._eventType === MediaTimelineRecord.EventType.CSSTransition);
|
||||
console.assert(!this._timestamps.length || timestamp > this._timestamps.lastValue.timestamp);
|
||||
|
||||
let type;
|
||||
switch (animationState) {
|
||||
case InspectorBackend.Enum.Animation.AnimationState.Ready:
|
||||
type = MediaTimelineRecord.TimestampType.CSSAnimationReady;
|
||||
break;
|
||||
case InspectorBackend.Enum.Animation.AnimationState.Delayed:
|
||||
type = MediaTimelineRecord.TimestampType.CSSAnimationDelay;
|
||||
break;
|
||||
case InspectorBackend.Enum.Animation.AnimationState.Active:
|
||||
type = MediaTimelineRecord.TimestampType.CSSAnimationActive;
|
||||
break;
|
||||
case InspectorBackend.Enum.Animation.AnimationState.Canceled:
|
||||
type = MediaTimelineRecord.TimestampType.CSSAnimationCancel;
|
||||
break;
|
||||
case InspectorBackend.Enum.Animation.AnimationState.Done:
|
||||
type = MediaTimelineRecord.TimestampType.CSSAnimationDone;
|
||||
break;
|
||||
}
|
||||
console.assert(type);
|
||||
|
||||
this._timestamps.push({type, timestamp});
|
||||
|
||||
this._updateTimes();
|
||||
}
|
||||
|
||||
addDOMEvent(timestamp, domEvent)
|
||||
{
|
||||
console.assert(this._eventType === MediaTimelineRecord.EventType.MediaElement);
|
||||
console.assert(!this._timestamps.length || timestamp > this._timestamps.lastValue.timestamp);
|
||||
|
||||
let data = {
|
||||
type: MediaTimelineRecord.TimestampType.MediaElementDOMEvent,
|
||||
timestamp,
|
||||
eventName: domEvent.eventName,
|
||||
};
|
||||
if (domEvent.originator instanceof WI.DOMNode) {
|
||||
data.originator = domEvent.originator;
|
||||
data.originatorDisplayName = data.originator.displayName;
|
||||
data.originatorCSSPath = WI.cssPath(data.originator, {full: true});
|
||||
}
|
||||
if (!isEmptyObject(domEvent.data))
|
||||
data.data = domEvent.data;
|
||||
this._timestamps.push(data);
|
||||
|
||||
this._updateTimes();
|
||||
}
|
||||
|
||||
powerEfficientPlaybackStateChanged(timestamp, isPowerEfficient)
|
||||
{
|
||||
console.assert(this._eventType === MediaTimelineRecord.EventType.MediaElement);
|
||||
console.assert(!this._timestamps.length || timestamp > this._timestamps.lastValue.timestamp);
|
||||
|
||||
this._timestamps.push({
|
||||
type: MediaTimelineRecord.TimestampType.MediaElementPowerEfficientPlaybackStateChange,
|
||||
timestamp,
|
||||
isPowerEfficient,
|
||||
});
|
||||
|
||||
this._updateTimes();
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_updateTimes()
|
||||
{
|
||||
let oldStartTime = this.startTime;
|
||||
let oldEndTime = this.endTime;
|
||||
|
||||
let firstItem = this._timestamps[0];
|
||||
let lastItem = this._timestamps.lastValue;
|
||||
|
||||
if (isNaN(this._startTime))
|
||||
this._startTime = firstItem.timestamp;
|
||||
|
||||
if (isNaN(this._activeStartTime)) {
|
||||
if (this._eventType === MediaTimelineRecord.EventType.MediaElement)
|
||||
this._activeStartTime = firstItem.timestamp;
|
||||
else if (firstItem.type === MediaTimelineRecord.TimestampType.CSSAnimationActive)
|
||||
this._activeStartTime = firstItem.timestamp;
|
||||
}
|
||||
|
||||
switch (lastItem.type) {
|
||||
case MediaTimelineRecord.TimestampType.CSSAnimationCancel:
|
||||
case MediaTimelineRecord.TimestampType.CSSAnimationDone:
|
||||
this._endTime = lastItem.timestamp;
|
||||
break;
|
||||
|
||||
case MediaTimelineRecord.TimestampType.MediaElementDOMEvent:
|
||||
if (WI.DOMNode.isPlayEvent(lastItem.eventName))
|
||||
this._endTime = NaN;
|
||||
else if (!isNaN(this._endTime) || WI.DOMNode.isPauseEvent(lastItem.eventName) || WI.DOMNode.isStopEvent(lastItem.eventName))
|
||||
this._endTime = lastItem.timestamp;
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.startTime !== oldStartTime || this.endTime !== oldEndTime)
|
||||
this.dispatchEventToListeners(WI.TimelineRecord.Event.Updated);
|
||||
}
|
||||
};
|
||||
|
||||
WI.MediaTimelineRecord.EventType = {
|
||||
CSSAnimation: "css-animation",
|
||||
CSSTransition: "css-transition",
|
||||
MediaElement: "media-element",
|
||||
};
|
||||
|
||||
WI.MediaTimelineRecord.TimestampType = {
|
||||
CSSAnimationReady: "css-animation-ready",
|
||||
CSSAnimationDelay: "css-animation-delay",
|
||||
CSSAnimationActive: "css-animation-active",
|
||||
CSSAnimationCancel: "css-animation-cancel",
|
||||
CSSAnimationDone: "css-animation-done",
|
||||
// CSS transitions share the same timestamp types.
|
||||
|
||||
MediaElementDOMEvent: "media-element-dom-event",
|
||||
MediaElementPowerEfficientPlaybackStateChange: "media-element-power-efficient-playback-state-change",
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.MemoryCategory = class MemoryCategory
|
||||
{
|
||||
constructor(type, size)
|
||||
{
|
||||
console.assert(typeof type === "string");
|
||||
console.assert(typeof size === "number");
|
||||
console.assert(size >= 0);
|
||||
|
||||
this.type = type;
|
||||
this.size = size;
|
||||
}
|
||||
};
|
||||
|
||||
WI.MemoryCategory.Type = {
|
||||
JavaScript: "javascript",
|
||||
Images: "images",
|
||||
Layers: "layers",
|
||||
Page: "page",
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.MemoryInstrument = class MemoryInstrument extends WI.Instrument
|
||||
{
|
||||
// Protected
|
||||
|
||||
get timelineRecordType()
|
||||
{
|
||||
return WI.TimelineRecord.Type.Memory;
|
||||
}
|
||||
|
||||
startInstrumentation(initiatedByBackend)
|
||||
{
|
||||
if (!initiatedByBackend) {
|
||||
let target = WI.assumingMainTarget();
|
||||
target.MemoryAgent.startTracking();
|
||||
}
|
||||
}
|
||||
|
||||
stopInstrumentation(initiatedByBackend)
|
||||
{
|
||||
if (!initiatedByBackend) {
|
||||
let target = WI.assumingMainTarget();
|
||||
target.MemoryAgent.stopTracking();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.MemoryPressureEvent = class MemoryPressureEvent
|
||||
{
|
||||
constructor(timestamp, severity)
|
||||
{
|
||||
this._timestamp = timestamp;
|
||||
this._severity = severity;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromPayload(timestamp, protocolSeverity)
|
||||
{
|
||||
let severity;
|
||||
switch (protocolSeverity) {
|
||||
case InspectorBackend.Enum.Memory.MemoryPressureSeverity.Critical:
|
||||
severity = WI.MemoryPressureEvent.Severity.Critical;
|
||||
break;
|
||||
case InspectorBackend.Enum.Memory.MemoryPressureSeverity.NonCritical:
|
||||
severity = WI.MemoryPressureEvent.Severity.NonCritical;
|
||||
break;
|
||||
default:
|
||||
console.error("Unexpected memory pressure severity", protocolSeverity);
|
||||
severity = WI.MemoryPressureEvent.Severity.NonCritical;
|
||||
break;
|
||||
}
|
||||
|
||||
return new WI.MemoryPressureEvent(timestamp, severity);
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
|
||||
static fromJSON(json)
|
||||
{
|
||||
let {timestamp, severity} = json;
|
||||
return new WI.MemoryPressureEvent(timestamp, severity);
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return {
|
||||
timestamp: this._timestamp,
|
||||
severity: this._severity,
|
||||
};
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get timestamp() { return this._timestamp; }
|
||||
get severity() { return this._severity; }
|
||||
};
|
||||
|
||||
WI.MemoryPressureEvent.Severity = {
|
||||
Critical: "critical",
|
||||
NonCritical: "non-critical",
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.MemoryTimeline = class MemoryTimeline extends WI.Timeline
|
||||
{
|
||||
// Public
|
||||
|
||||
get memoryPressureEvents() { return this._pressureEvents; }
|
||||
|
||||
addMemoryPressureEvent(memoryPressureEvent)
|
||||
{
|
||||
console.assert(memoryPressureEvent instanceof WI.MemoryPressureEvent);
|
||||
|
||||
this._pressureEvents.push(memoryPressureEvent);
|
||||
|
||||
this.dispatchEventToListeners(WI.MemoryTimeline.Event.MemoryPressureEventAdded, {memoryPressureEvent});
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
reset(suppressEvents)
|
||||
{
|
||||
super.reset(suppressEvents);
|
||||
|
||||
this._pressureEvents = [];
|
||||
}
|
||||
|
||||
addRecord(record, options = {})
|
||||
{
|
||||
let lastRecord = this.records.lastValue;
|
||||
if (lastRecord) {
|
||||
let startTime = lastRecord.endTime;
|
||||
if (options.discontinuity)
|
||||
startTime = options.discontinuity.endTime;
|
||||
record.adjustStartTime(startTime);
|
||||
}
|
||||
|
||||
super.addRecord(record, options);
|
||||
}
|
||||
};
|
||||
|
||||
WI.MemoryTimeline.Event = {
|
||||
MemoryPressureEventAdded: "memory-timeline-memory-pressure-event-added",
|
||||
};
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.MemoryTimelineRecord = class MemoryTimelineRecord extends WI.TimelineRecord
|
||||
{
|
||||
constructor(timestamp, categories)
|
||||
{
|
||||
super(WI.TimelineRecord.Type.Memory, timestamp - MemoryTimelineRecord.samplingRatePerSecond, timestamp);
|
||||
|
||||
console.assert(typeof timestamp === "number");
|
||||
console.assert(categories instanceof Array);
|
||||
|
||||
this._timestamp = timestamp;
|
||||
this._categories = WI.MemoryTimelineRecord.memoryCategoriesFromProtocol(categories);
|
||||
this._exportCategories = categories;
|
||||
|
||||
this._totalSize = 0;
|
||||
for (let {size} of categories)
|
||||
this._totalSize += size;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static get samplingRatePerSecond()
|
||||
{
|
||||
// 500ms. This matches the ResourceUsageThread sampling frequency in the backend.
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
static memoryCategoriesFromProtocol(categories)
|
||||
{
|
||||
let javascriptSize = 0;
|
||||
let imagesSize = 0;
|
||||
let layersSize = 0;
|
||||
let pageSize = 0;
|
||||
|
||||
for (let {type, size} of categories) {
|
||||
switch (type) {
|
||||
case InspectorBackend.Enum.Memory.CategoryDataType.JavaScript:
|
||||
case InspectorBackend.Enum.Memory.CategoryDataType.JIT:
|
||||
javascriptSize += size;
|
||||
break;
|
||||
case InspectorBackend.Enum.Memory.CategoryDataType.Images:
|
||||
imagesSize += size;
|
||||
break;
|
||||
case InspectorBackend.Enum.Memory.CategoryDataType.Layers:
|
||||
layersSize += size;
|
||||
break;
|
||||
case InspectorBackend.Enum.Memory.CategoryDataType.Page:
|
||||
case InspectorBackend.Enum.Memory.CategoryDataType.Other:
|
||||
pageSize += size;
|
||||
break;
|
||||
default:
|
||||
console.warn("Unhandled Memory.CategoryDataType: " + type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let memoryCategories = [];
|
||||
if (javascriptSize)
|
||||
memoryCategories.push({type: WI.MemoryCategory.Type.JavaScript, size: javascriptSize});
|
||||
if (imagesSize)
|
||||
memoryCategories.push({type: WI.MemoryCategory.Type.Images, size: imagesSize});
|
||||
if (layersSize)
|
||||
memoryCategories.push({type: WI.MemoryCategory.Type.Layers, size: layersSize});
|
||||
if (pageSize)
|
||||
memoryCategories.push({type: WI.MemoryCategory.Type.Page, size: pageSize});
|
||||
return memoryCategories;
|
||||
}
|
||||
|
||||
// Import / Export
|
||||
|
||||
static async fromJSON(json)
|
||||
{
|
||||
let {timestamp, categories} = json;
|
||||
return new WI.MemoryTimelineRecord(timestamp, categories);
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return {
|
||||
type: this.type,
|
||||
timestamp: this.startTime,
|
||||
categories: this._exportCategories,
|
||||
};
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get timestamp() { return this._timestamp; }
|
||||
get categories() { return this._categories; }
|
||||
get totalSize() { return this._totalSize; }
|
||||
|
||||
get unadjustedStartTime() { return this._timestamp; }
|
||||
|
||||
adjustStartTime(startTime)
|
||||
{
|
||||
console.assert(startTime < this._endTime);
|
||||
this._startTime = startTime;
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.NetworkInstrument = class NetworkInstrument extends WI.Instrument
|
||||
{
|
||||
// Protected
|
||||
|
||||
get timelineRecordType()
|
||||
{
|
||||
return WI.TimelineRecord.Type.Network;
|
||||
}
|
||||
|
||||
startInstrumentation(initiatedByBackend)
|
||||
{
|
||||
// Nothing to do, network instrumentation is always happening.
|
||||
}
|
||||
|
||||
stopInstrumentation(initiatedByBackend)
|
||||
{
|
||||
// Nothing to do, network instrumentation is always happening.
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.NetworkTimeline = class NetworkTimeline extends WI.Timeline
|
||||
{
|
||||
// Public
|
||||
|
||||
recordForResource(resource)
|
||||
{
|
||||
console.assert(resource instanceof WI.Resource);
|
||||
|
||||
return this._resourceRecordMap.get(resource) || null;
|
||||
}
|
||||
|
||||
reset(suppressEvents)
|
||||
{
|
||||
this._resourceRecordMap = new Map;
|
||||
|
||||
super.reset(suppressEvents);
|
||||
}
|
||||
|
||||
addRecord(record, options = {})
|
||||
{
|
||||
console.assert(record instanceof WI.ResourceTimelineRecord);
|
||||
|
||||
// Don't allow duplicate records for a resource.
|
||||
if (this._resourceRecordMap.has(record.resource))
|
||||
return;
|
||||
|
||||
this._resourceRecordMap.set(record.resource, record);
|
||||
|
||||
super.addRecord(record, options);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.ObjectPreview = class ObjectPreview
|
||||
{
|
||||
constructor(type, subtype, description, lossless, overflow, properties, entries, size)
|
||||
{
|
||||
console.assert(type);
|
||||
console.assert(typeof lossless === "boolean");
|
||||
console.assert(!properties || !properties.length || properties[0] instanceof WI.PropertyPreview);
|
||||
console.assert(!entries || !entries.length || entries[0] instanceof WI.CollectionEntryPreview);
|
||||
|
||||
this._type = type;
|
||||
this._subtype = subtype;
|
||||
this._description = description || "";
|
||||
this._lossless = lossless;
|
||||
this._overflow = overflow || false;
|
||||
this._size = size;
|
||||
|
||||
this._properties = properties || null;
|
||||
this._entries = entries || null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
// Runtime.ObjectPreview.
|
||||
static fromPayload(payload)
|
||||
{
|
||||
if (payload.properties)
|
||||
payload.properties = payload.properties.map(WI.PropertyPreview.fromPayload);
|
||||
if (payload.entries)
|
||||
payload.entries = payload.entries.map(WI.CollectionEntryPreview.fromPayload);
|
||||
return new WI.ObjectPreview(payload.type, payload.subtype, payload.description, payload.lossless, payload.overflow, payload.properties, payload.entries, payload.size);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get type() { return this._type; }
|
||||
get subtype() { return this._subtype; }
|
||||
get description() { return this._description; }
|
||||
get lossless() { return this._lossless; }
|
||||
get overflow() { return this._overflow; }
|
||||
get propertyPreviews() { return this._properties; }
|
||||
get collectionEntryPreviews() { return this._entries; }
|
||||
get size() { return this._size; }
|
||||
|
||||
hasSize()
|
||||
{
|
||||
return this._size !== undefined && (this._subtype === "array" || this._subtype === "set" || this._subtype === "map" || this._subtype === "weakmap" || this._subtype === "weakset");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2013 University of Washington. All rights reserved.
|
||||
* Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.ProbeSample = class ProbeSample
|
||||
{
|
||||
constructor(sampleId, batchId, elapsedTime, object)
|
||||
{
|
||||
console.assert(object instanceof WI.RemoteObject);
|
||||
|
||||
this.sampleId = sampleId;
|
||||
this.batchId = batchId;
|
||||
this.timestamp = elapsedTime;
|
||||
this.object = object;
|
||||
}
|
||||
};
|
||||
|
||||
WI.Probe = class Probe extends WI.Object
|
||||
{
|
||||
constructor(id, breakpoint, expression)
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(id);
|
||||
console.assert(breakpoint instanceof WI.Breakpoint);
|
||||
|
||||
this._id = id;
|
||||
this._breakpoint = breakpoint;
|
||||
this._expression = expression;
|
||||
this._samples = [];
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get id()
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get breakpoint()
|
||||
{
|
||||
return this._breakpoint;
|
||||
}
|
||||
|
||||
get expression()
|
||||
{
|
||||
return this._expression;
|
||||
}
|
||||
|
||||
set expression(value)
|
||||
{
|
||||
if (this._expression === value)
|
||||
return;
|
||||
|
||||
var data = {oldValue: this._expression, newValue: value};
|
||||
this._expression = value;
|
||||
this.clearSamples();
|
||||
this.dispatchEventToListeners(WI.Probe.Event.ExpressionChanged, data);
|
||||
}
|
||||
|
||||
get samples()
|
||||
{
|
||||
return this._samples.slice();
|
||||
}
|
||||
|
||||
clearSamples()
|
||||
{
|
||||
this._samples = [];
|
||||
this.dispatchEventToListeners(WI.Probe.Event.SamplesCleared);
|
||||
}
|
||||
|
||||
addSample(sample)
|
||||
{
|
||||
console.assert(sample instanceof WI.ProbeSample, "Wrong object type passed as probe sample: ", sample);
|
||||
this._samples.push(sample);
|
||||
this.dispatchEventToListeners(WI.Probe.Event.SampleAdded, sample);
|
||||
}
|
||||
};
|
||||
|
||||
WI.Probe.Event = {
|
||||
ExpressionChanged: "probe-object-expression-changed",
|
||||
SampleAdded: "probe-object-sample-added",
|
||||
SamplesCleared: "probe-object-samples-cleared"
|
||||
};
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2013 University of Washington. All rights reserved.
|
||||
* Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// A ProbeSet clusters Probes from the same Breakpoint and their samples.
|
||||
|
||||
WI.ProbeSet = class ProbeSet extends WI.Object
|
||||
{
|
||||
constructor(breakpoint)
|
||||
{
|
||||
super();
|
||||
|
||||
console.assert(breakpoint instanceof WI.Breakpoint, "Unknown breakpoint argument: ", breakpoint);
|
||||
|
||||
this._breakpoint = breakpoint;
|
||||
this._probes = [];
|
||||
this._probesByIdentifier = new Map;
|
||||
|
||||
this._createDataTable();
|
||||
|
||||
WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceChanged, this);
|
||||
WI.Probe.addEventListener(WI.Probe.Event.SampleAdded, this._sampleCollected, this);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get breakpoint() { return this._breakpoint; }
|
||||
get probes() { return this._probes.slice(); }
|
||||
get dataTable() { return this._dataTable; }
|
||||
|
||||
clear()
|
||||
{
|
||||
this._breakpoint.clearActions(WI.BreakpointAction.Type.Probe);
|
||||
}
|
||||
|
||||
clearSamples()
|
||||
{
|
||||
for (var probe of this._probes)
|
||||
probe.clearSamples();
|
||||
|
||||
var oldTable = this._dataTable;
|
||||
this._createDataTable();
|
||||
this.dispatchEventToListeners(WI.ProbeSet.Event.SamplesCleared, {oldTable});
|
||||
}
|
||||
|
||||
createProbe(expression)
|
||||
{
|
||||
this._breakpoint.addAction(new WI.BreakpointAction(WI.BreakpointAction.Type.Probe, {data: expression}));
|
||||
}
|
||||
|
||||
addProbe(probe)
|
||||
{
|
||||
console.assert(probe instanceof WI.Probe, "Tried to add non-probe ", probe, " to probe group", this);
|
||||
console.assert(probe.breakpoint === this.breakpoint, "Probe and ProbeSet must have same breakpoint.", probe, this);
|
||||
|
||||
this._probes.push(probe);
|
||||
this._probesByIdentifier.set(probe.id, probe);
|
||||
|
||||
this.dataTable.addProbe(probe);
|
||||
this.dispatchEventToListeners(WI.ProbeSet.Event.ProbeAdded, probe);
|
||||
}
|
||||
|
||||
removeProbe(probe)
|
||||
{
|
||||
console.assert(probe instanceof WI.Probe, "Tried to remove non-probe ", probe, " to probe group", this);
|
||||
console.assert(this._probes.indexOf(probe) !== -1, "Tried to remove probe", probe, " not in group ", this);
|
||||
console.assert(this._probesByIdentifier.has(probe.id), "Tried to remove probe", probe, " not in group ", this);
|
||||
|
||||
this._probes.splice(this._probes.indexOf(probe), 1);
|
||||
this._probesByIdentifier.delete(probe.id);
|
||||
this.dataTable.removeProbe(probe);
|
||||
this.dispatchEventToListeners(WI.ProbeSet.Event.ProbeRemoved, probe);
|
||||
}
|
||||
|
||||
willRemove()
|
||||
{
|
||||
console.assert(!this._probes.length, "ProbeSet.willRemove called, but probes still associated with group: ", this._probes);
|
||||
|
||||
WI.Frame.removeEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceChanged, this);
|
||||
WI.Probe.removeEventListener(WI.Probe.Event.SampleAdded, this._sampleCollected, this);
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_mainResourceChanged()
|
||||
{
|
||||
this.dataTable.mainResourceChanged();
|
||||
}
|
||||
|
||||
_createDataTable()
|
||||
{
|
||||
if (this.dataTable)
|
||||
this.dataTable.willRemove();
|
||||
|
||||
this._dataTable = new WI.ProbeSetDataTable(this);
|
||||
}
|
||||
|
||||
_sampleCollected(event)
|
||||
{
|
||||
var sample = event.data;
|
||||
console.assert(sample instanceof WI.ProbeSample, "Tried to add non-sample to probe group: ", sample);
|
||||
|
||||
var probe = event.target;
|
||||
if (!this._probesByIdentifier.has(probe.id))
|
||||
return;
|
||||
|
||||
console.assert(this.dataTable);
|
||||
this.dataTable.addSampleForProbe(probe, sample);
|
||||
this.dispatchEventToListeners(WI.ProbeSet.Event.SampleAdded, {probe, sample});
|
||||
}
|
||||
};
|
||||
|
||||
WI.ProbeSet.Event = {
|
||||
ProbeAdded: "probe-set-probe-added",
|
||||
ProbeRemoved: "probe-set-probe-removed",
|
||||
SampleAdded: "probe-set-sample-added",
|
||||
SamplesCleared: "probe-set-samples-cleared",
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user