Added MacOS SDK

This commit is contained in:
Andrew Zambazos
2026-06-11 14:04:52 +12:00
parent ffdc88608e
commit 553ab6537a
2151 changed files with 450464 additions and 0 deletions
+84
View File
@@ -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",
};
+316
View File
@@ -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",
};
+113
View File
@@ -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);
}
};
+54
View File
@@ -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; }
};
+411
View File
@@ -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",
};
+368
View File
@@ -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";
+252
View File
@@ -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",
};
+333
View File
@@ -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",
};
+137
View File
@@ -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";
+97
View File
@@ -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;
}
};
+154
View File
@@ -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;
}
};
+191
View File
@@ -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(" ");
}
};
+144
View File
@@ -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;
}
};
+327
View File
@@ -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",
};
+143
View File
@@ -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",
};
+65
View File
@@ -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();
}
}
};
+42
View File
@@ -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);
}
};
+114
View File
@@ -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;
}
};
+264
View File
@@ -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",
]);
+162
View File
@@ -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
+49
View File
@@ -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"
};
+672
View File
@@ -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;
}
};
+145
View File
@@ -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);
}
};
+47
View File
@@ -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));
}
};
+629
View File
@@ -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",
};
+234
View File
@@ -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"
};
+279
View File
@@ -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),
});
}
};
+174
View File
@@ -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"),
};
+242
View File
@@ -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;
+494
View File
@@ -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",
};
+129
View File
@@ -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",
};
+54
View File
@@ -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; }
};
+116
View File
@@ -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;
+146
View File
@@ -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",
};
+109
View File
@@ -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;
}
};
+322
View File
@@ -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",
};
+68
View File
@@ -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";
+152
View File
@@ -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
+175
View File
@@ -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";
+148
View File
@@ -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",
};
+55
View File
@@ -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;
}
};
+239
View File
@@ -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",
};
+95
View File
@@ -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";
+48
View File
@@ -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";
+28
View File
@@ -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
{
};
+149
View File
@@ -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;
}
}
};
+299
View File
@@ -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"
};
+217
View File
@@ -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;
+77
View File
@@ -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",
};
+68
View File
@@ -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;
}
};
+34
View File
@@ -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;
}
};
+61
View File
@@ -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);
}
};
+351
View File
@@ -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;
+53
View File
@@ -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; }
};
+512
View File
@@ -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";
+81
View File
@@ -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",
};
+660
View File
@@ -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],
};
+498
View File
@@ -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; }
};
+176
View File
@@ -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",
};
+57
View File
@@ -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";
+107
View File
@@ -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;
+243
View File
@@ -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
};
+279
View File
@@ -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;
+267
View File
@@ -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, "'")
};
+83
View File
@@ -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; }
};
+34
View File
@@ -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;
}
};
+133
View File
@@ -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";
+105
View File
@@ -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;
}
}
};
+59
View File
@@ -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();
}
};
+334
View File
@@ -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",
};
+328
View File
@@ -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",
};
+66
View File
@@ -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);
}
};
+28
View File
@@ -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
{
};
+57
View File
@@ -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",
};
+75
View File
@@ -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();
}
}
}
};
+66
View File
@@ -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);
}
};
+314
View File
@@ -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",
};
+44
View File
@@ -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",
};
+50
View File
@@ -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();
}
}
};
+81
View File
@@ -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",
};
+66
View File
@@ -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",
};
+123
View File
@@ -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
+44
View File
@@ -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.
}
};
+56
View File
@@ -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);
}
};
+73
View File
@@ -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");
}
};
+106
View File
@@ -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"
};
+140
View File
@@ -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