diff --git a/docs/Deep Dive/Web Inspector/AnIntroduction.md b/docs/Deep Dive/Web Inspector/AnIntroduction.md new file mode 100644 index 00000000..5f063d09 --- /dev/null +++ b/docs/Deep Dive/Web Inspector/AnIntroduction.md @@ -0,0 +1,144 @@ +# Introduction + +The Web Inspector allows you to view the page source, live DOM hierarchy, script debugging, profiling and more! + +## Enabling the Web Inspector + +### Safari + +* Enable the Develop menu option in the Advanced preferences. +* Use the optional toolbar button, Develop menu or Inspect Element context menu to access the Web Inspector. + +### Other WebKit clients + +* Find the application's bundle identifier. +* Enter the following command once in Terminal (inserting the bundle identifier) + +``` +defaults write "bundle-identifier-here" WebKitDeveloperExtras -bool true +``` + +* Relaunch the application in order to use the Web Inspector + +## Using the Web Inspector + +The Web Inspector can be opened by '''right clicking anywhere on a web page''' and choosing '''Inspect Element'''. Once open, it highlights the node on the page as it is selected in the hierarchy. You can also search for nodes by node name, id and CSS class name. + +The Node pane shows the current node type and name, as well as any element attributes. + +Under the Style pane we show all the CSS rules that apply to the focused node. These rules are listed in cascade order with overridden properties striked-out—letting you truly see how cascading stylesheets affect the page layout. All shorthand properties have a disclosure-triangle to show and hide the expanded properties created by the shorthand. + +The Metrics pane provides a quick visual look at how margins, borders and padding affect the current node. + +Various HTML and JavaScript properties, including length of text nodes, offsetWidth/Height, class names, and parent/sibling information are vieweable in the Properties pane. + +See [Safari User Guide for Web Developers](http://developer.apple.com/safari/library/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/UsingtheWebInspector/UsingtheWebInspector.html) for more details on other panels of the Web Inspector. + +## Hacking on the Web Inspector + +Most of the Web Inspector's code is HTML, JavaScript, and CSS—so it's very easy to implement new features and fix bugs! + +[List Web Inspector bugs and feature requests](http://tinyurl.com/2vqypl) + +## Related Blog Posts + +* [Introducing the Web Inspector](http://webkit.org/blog/41/introducing-the-web-inspector) +* [Yet another one more thing… a new Web Inspector!](http://webkit.org/blog/108/yet-another-one-more-thing-a-new-web-inspector) +* [Web Inspector Redesign](http://webkit.org/blog/197/web-inspector-redesign) +* [Web Inspector Updates](http://webkit.org/blog/829/web-inspector-updates/) +* [State of Web Inspector](https://www.webkit.org/blog/2518/state-of-web-inspector/) + +## Shortcut Keys + +### Safari + +| | Mac | Windows / Linux | +|----------------------------|-------|------------------| +| Toggle Web Inspector | ⌥⌘I | Ctrl-Alt-I | +| Show Error Console | ⌥⌘C | Ctrl-Alt-C | +| Start Profiling Javascript | ⌥⇧⌘P | Ctrl-Alt-P | + +### Web Inspector + +| | Mac | Windows / Linux | +|-----------------------------|-----|-----------------| +| Next Panel | ⌘] | Ctrl-] | +| Previous Panel | ⌘[ | Ctrl-[ | +| Toggle Console | ⎋ | Esc | +| Focus Search Box | ⌘F | Ctrl-F | +| Find Next | ⌘G | Ctrl-G | +| Find Previous | ⇧⌘G | Ctrl-Shift-G | + +### Console + +| | Mac | Windows / Linux | +|-----------------------------|----------|-----------------| +| Next Suggestion | ⇥ | Tab | +| Previous Suggestion | ⇧⇥ | Shift-Tab | +| Accept Suggestion | → | Right | +| Previous Command / Line | ↑ | Up | +| Next Command / Line | ↓ | Down | +| Previous Command | ⌃P | | +| Next Command | ⌃N | | +| Clear History | ⌘K or ⌃L | Ctrl-L | +| Execute | ⏎ | Enter | + +### Elements Panel + +| | Mac | Windows / Linux | +|-----------------------------|--------|-----------------| +| Navigate | ↑ ↓ | Up/Down | +| Expand/Collapse Node | ← → | Right/Left | +| Expand Node | Double-Click on tag | Double-Click on tag | +| Edit Attribute | ⏎ or Double-Click on attribute | Enter or Double-Click on attribute | + +### Styles Pane + +| | Mac | Windows / Linux | +|-----------------------------------|----------------------------|-----------------| +| Edit Rule | Double-Click | Double-Click | +| Edit Next/Previous Property | ⇥ / ⇧⇥ | Tab/Shift-Tab | +| Insert New Property | Double-Click on whitespace | Double-Click on whitespace | +| Increment/Decrement Value | ⌥↑ /⌥ ↓ | Alt- Up/Alt-Down | +| Increment/Decrement Value by 10 | ⌥⇧↑ / ⌥⇧↓ | Alt-Shift-Up/Alt-Shift-Down | +| Increment/Decrement Value by 10 | ⌥PageUp / ⌥PageDown | Alt-PageUp/Alt-PageDown | +| Increment/Decrement Value by 100 | ⌥⇧PageUp / ⌥⇧PageDown | Shift-PageUp/Shift-PageDown | +| Increment/Decrement Value by 0.1 | ⌃⌥↑ / ⌃⌥↓ | Control-Alt-Up/Control-Alt-Down | + +### Debugger + +| | Mac | Windows / Linux | +|-------------------------------|--------------|------------------| +| Select Next Call Frame | ⌃. | Ctrl-. | +| Select Previous Call Frame | ⌃, | Ctrl-, | +| Continue | F8 or ⌘/ | F8 or Ctrl-/ | +| Step Over | F10 or ⌘' | F10 or Ctrl-' | +| Step Into | F11 or ⌘; | F11 or Ctrl-; | +| Step Out | ⇧F11 or ⇧⌘; | Shift-F11 or Ctrl-Shift-; | +| Evaluate Selection | ⇧⌘E | Ctrl-Shift-E | +| Toggle Breakpoint Condition | Click on line number | Click on line number | +| Edit Breakpoint Condition | Right-Click on line number | Right-Click on line number | + + +## Using the Web Inspector remotely + +### Remote Web Inspector on GTK+ and EFL ports + + * For the GTK and EFL ports, this can be enabled with an environment variable. Check the documentation on EnvironmentVariables + +``` +Computer1 # export WEBKIT_INSPECTOR_SERVER=${ip.ad.dre.ss}:${port} +Computer1 # MiniBrowser http://google.com +# +Computer2 # MiniBrowser http://${ip.ad.dre.ss}:${port} +``` + + * The very same version of WebKit has to be used on the other side + +### Apple Web Inspector Remote Experiment + +[Safari User Guide for Web Developers](http://developer.apple.com/safari/library/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/UsingtheWebInspector/UsingtheWebInspector.html) +In early 2010, an experiment was made to get Web Inspector to run in a plain old web page, debugging a remote web browsing session in another browser window. + +A write-up is available here: +[weinre - Web Inspector Remote](http://muellerware.org/papers/weinre/manual.html), and the relevant source and demo archives are attached to this page. diff --git a/docs/Deep Dive/Web Inspector/Contributing.md b/docs/Deep Dive/Web Inspector/Contributing.md new file mode 100644 index 00000000..e9c5272e --- /dev/null +++ b/docs/Deep Dive/Web Inspector/Contributing.md @@ -0,0 +1,84 @@ +# Contributing to Web Inspector + +## Modifying the Web Inspector + +The Web Inspector user interface is implemented using JavaScript, CSS, and HTML. So, it's relatively easy to dig into the Web Inspector's sources and fix bugs or add new features. + +This wiki page documents the minimal steps required to modify styles used by the Web Inspector and submit your changes as a patch for review. + +Let's say, we don't like red color for CSS property names, and we would prefer property names to be purple instead. Let's get started! + +## Inspect The Inspector + +Since the Web Inspector UI is just another web page, we can inspect the Web Inspector with a second-level Web Inspector instance to quickly see what's going on. This requires a few magic settings to enable the "Inspect..." context menu on the Web Inspector window. + +For the Mac port, set the following defaults to allow inspecting the inspector. + +``` +defaults write NSGlobalDomain WebKitDebugDeveloperExtrasEnabled -bool YES +``` + + +After updating these settings, run the [https://developer.apple.com/safari/technology-preview/ Safari Technology Preview]. Then, open the Web Inspector and right-click to inspect the inspector itself. + +By inspecting the CSS property names in the second-level inspector, we quickly find that the colors are defined by rules in `Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css`. +To create and submit a patch with our changes, we must to create an accompanying Bugzilla bug, and compute the diff of our changes against WebKit trunk. + +## Create / Update a Bug Report + + * [Existing Web Inspector Bugs](https://bugs.webkit.org/buglist.cgi?query_format=advanced&short_desc_type=allwordssubstr&short_desc=&component=Web+Inspector&long_desc_type=substring&long_desc=&bug_file_loc_type=allwordssubstr&bug_file_loc=&keywords_type=allwords&keywords=&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&emailassigned_to1=1&emailtype1=substring&email1=&emailassigned_to2=1&emailreporter2=1&emailcc2=1&emailtype2=substring&email2=&bugidtype=include&bug_id=&votes=&chfieldfrom=&chfieldto=Now&chfieldvalue=&cmdtype=doit&order=Reuse+same+sort+as+last+time&field0-0-0=noop&type0-0-0=noop&value0-0-0=) + * [Create New Web Inspector Bug](http://webkit.org/new-inspector-bug) + +The WebKit project uses "bugs" in Bugzilla for fixes, new features, and any other code changes. Every commit must have an accompanying Bugzilla bug. + +So, the first step is to ensure that your proposed enhancement or fix has an associated bug. + +Once you find or create a bug report, make sure to add a comment stating your intent to work on the bug. +This step is very important; comments on bugs in the Web Inspector Bugzilla component will automatically notify Web Inspector reviewers. + +This will allow them to answer any questions you may have about a proposed fix, and give feedback, pointers, and guidance for solving the issue. + +## Now Do Your Hacking + + 1. [Get the Code](https://webkit.org/getting-the-code/) + +``` +git clone https://github.com/WebKit/WebKit.git WebKit +cd WebKit +git checkout -b purple_css_values +``` + + 2. [Build WebKit](http://webkit.org/building/build.html) + +``` +Tools/Scripts/build-webkit --release +``` + + A clean build takes 20-80 minutes depending on the vintage of your machine. + + 3. [Run it](http://webkit.org/building/run.html) + +``` +Tools/Scripts/run-minibrowser --release +``` + + 4. Edit `Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css` within the repo. + + 5. Run `make -C Source/WebInspectorUI release` to copy files from `Source/WebInspectorUI/UserInterface` to the build directory. Do it after every time you modify Inspector's files. + + 6. Look at your changes + +``` +git status +git diff Source/WebInspectorUI/ +``` + + 7. [Submit a PR](https://webkit.org/contributing-code/#overview) and wait for a review! + +``` +git add -u +git commit +Tools/Scripts/git-webkit pull-request +``` + +If you have any questions there are always people willing to help! Just jump onto [webkit.slack.com](https://webkit.slack.com), #webkit-inspector channel. diff --git a/docs/Deep Dive/Web Inspector/RemoteInspectorWebKitGTKandWPE.md b/docs/Deep Dive/Web Inspector/RemoteInspectorWebKitGTKandWPE.md new file mode 100644 index 00000000..b1a6ceda --- /dev/null +++ b/docs/Deep Dive/Web Inspector/RemoteInspectorWebKitGTKandWPE.md @@ -0,0 +1,22 @@ +# Using the Remote Inspector with WebKitGTK+ and WPE + +The remote inspector enables debugging of web pages in environment where you might not be able to run the web inspector directly, such as WPE running in embedded targets. + +To run the remote inspector, you need to: + +- set the environment variable `WEBKIT_INSPECTOR_SERVER=ip:port` before running jsc or a browser/launcher powered by WPE or WebKitGTK+ +- enable the WebKitSettings `enable-developer-extras` + +For example: + +``` +export WEBKIT_INSPECTOR_SERVER=192.168.0.50:5000 +MiniBrowser --enable-developer-extras=true https://wpewebkit.org +``` + +Then, open another browser with the same version of WebKitGTK+ (matching the WPE version if it's the case) and open `inspector://ip:port`: + + +``` +MiniBrowser inspector://192.168.0.50:5000 +``` diff --git a/docs/Deep Dive/Web Inspector/WebInspectorCodeStyle.md b/docs/Deep Dive/Web Inspector/WebInspectorCodeStyle.md new file mode 100644 index 00000000..24b4e400 --- /dev/null +++ b/docs/Deep Dive/Web Inspector/WebInspectorCodeStyle.md @@ -0,0 +1,281 @@ +# Web Inspector Syle Guide + +These are JavaScript coding styles used in the [https://trac.webkit.org/browser/trunk/Source/WebInspectorUI/ Source/WebInspectorUI/UserInterface](https://trac.webkit.org/browser/trunk/Source/WebInspectorUI/ Source/WebInspectorUI/UserInterface) folder. + +(Note: Few if any of these guidelines are checked by `check-webkit-style`. There's a tracking bug for that: [https://bugs.webkit.org/show_bug.cgi?id=125045](https://bugs.webkit.org/show_bug.cgi?id=125045)) + +## Non-code Style + +For user-facing strings, we follow the macOS Human Interface Guidelines. + +* [Guidelines for help tags](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/Assistance.html) (aka tooltips) +* [Guidelines for UI labels](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/TerminologyWording.html#//apple_ref/doc/uid/20000957-CH15-SW4) +* [Guidelines for keyboard shortcuts](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/Keyboard.html#//apple_ref/doc/uid/20000957-CH84-SW1) + +## Localization + +* Include a unique key and comment when dealing with arbitrary strings. Use `@` in the key to denote where in the interface the string can be found. (ex: `WI.UIString("Frames")` vs `WI.UIString("Frames", "Frames @ Execution Context Picker", "Title for list of HTML subframe JavaScript execution contexts")`). + +## Tokens, spacing, indentation, syntax + +* No trailing whitespace. +* Indent with 4 spaces. +* Double quoted strings; use template strings if a bunch of interpolation is required. +* The `{` after a named, non-inlined function goes on the next line. Anywhere else, the `{` stays on the same line. +* Style for object literals is: `{key1: value1, key2: value2}`. When key and variable names coincide, use the syntax `{key}` rather than `{key: key}`. If the object is complex enough, each `key: value,` should be on its own line. +* Always include a trailing comma for object literals. +* Add new lines before and after different tasks performed in the same function. +* Else-blocks should share a line with leading `}`. +* Long promise chains should place `.then()` blocks on a new line. +* Calling a constructor with no arguments should have no parenthesis `()`. (ex: `var map = new Map;`) +* Put anonymous functions inline if they are not used as a subroutine. +* Prefer `let` to `var`, unless the variable is not used in a block scoping manner. Be careful when using `let` with `case` switches, as [all switch cases share the same block by default](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone_and_errors_with_let). Only use `const` for values that will not change between executions (i.e. actual constants). +* Use arrow functions when possible, unless it makes code less readable. See below for examples. +* For default parameters, add a space around the default assignment: `function foo(isGood = false)` +* Trivial public getters can be made a single line and moved to the top of the list of getters in a class, unless there is a corresponding setter. + +## Naming things + +* Avoid using the "on" prefix where possible. The `_onFoo` methods can just be `_foo` or `_handleFoo` (preferred for event listeners). +* New class names should use the name of the base class as a suffix. (ex: `TimelinesContentView` < `ContentView`). Exceptions: classes extending `WI.Object` (unless they are a represented object). +* Spell out `identifier` instead of `id` if not doing so would result in a name ending with capitalized `Id`. For example, just `this.id` is fine, but `this.breakpointId` should be `this.breakpointIdentifier`. +* An object's events live on the `Event` property of the constructor. Event names are properties on the `Event` object, and property values duplicate the event name, but are lowercased, hyphenated, and prefixed with the constructor name. See the skeleton example below. +* When serializing a function to be evaluated in a different execution context, such as from inspector to inspected page or layout test to inspector, make it obvious where the function is going to be evaluated. For example, if a function will be sent from Inspector context to inspected page context, the name `inspectedPage_node_getFlowInfo()` signifies that the function will be evaluated in the inspected page, with `this` bound to a node, and it performs the action `getFlowInfo`. + +## API preferences + +* Use `Map` and `Set` collections instead of plain objects if the key values are unknown or not monotonic (i.e., frequently added then removed). +* Use `hsla()` over hex or `rgba()` for colors in CSS. +* Use `for..of` syntax when performing actions on each element. Use `forEach` when chaining methods in a functional style. Use a classical for loop when doing index math. +* When using `forEach` or `map`, use an arrow function or supply the `this`-object as the optional second parameter rather than binding it. +* In promise chains, use arrow functions for lexical `this`, rather than assigning `const instance = this;' or `.bind`ing every function's `this`-argument. +* Use destructuring assignment when digging values out of a JSON object or "args" object. +* Use default parameters when it makes sense. +* Use `super` to make calls to base class (possibly overridden) methods. + +## Layering and abstractions + +* Firewall the protocol inside the Manager classes. JSON objects received from the protocol are called "payload" in the code. The payload is usually deconstructed at the Managers level and passes down as smart objects inheriting from `WI.Object`. +* Avoid accessing *View classes from *Manager or *Object classes. This is a layering violation that prevents writing tests for models. +* Avoid storing DOM elements in *Manager or *Object classes. (see above.) +* In the backend, avoid using Inspector TypeBuilders outside of InspectorAgent classes. We want to isolate protocol considerations from other functionality in JavaScriptCore and WebCore. + +## Understanding and Using Promises + +[What's so great about Promises?](http://blog.parse.com/2013/01/29/whats-so-great-about-javascript-promises/) [The point of promises is to give us back functional composition](http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/) and error bubbling in the async world. They do this by saying that your functions should return a promise, which can do one of two things: + +1. Become __fulfilled__ by a **value** +2. Become __rejected__ with an **Error instance** or by throwing an exception + +A promise that is eiher fulfilled or rejected is said to be __settled__. A promise that has not settled is said to be __pending__. + +And, if you have a correctly implemented `then()` function, then fulfillment and rejection will compose just like their synchronous counterparts, with fulfillments flowing up a compositional chain, but being interrupted at any time by a rejection that is only handled by someone who declares they are ready to handle it. + +### Promise Gotchas + +(Summarized from [change.org Blog](http://making.change.org/post/69613524472/promises-and-error-handling) and [The Art of Code Blog](http://taoofcode.net/promise-anti-patterns/)) + +* Don't nest promises to perform multiple async operations; instead, chain them or use `Promise.all()`. +* Beware of storing or returning promise values that are not from the end of a chain. Each `.then()` returns a new promise value, so return the last promise. +* Use `Promise.all()` with `map()` to process an array of asynchronous work in parallel. Use `Promise.all()` with `reduce()` to sequence an array asynchronous work. +* If a result may be a promise or an actual value, wrap the value in a promise, e.g., `Promise.resolve(val)` +* Use `.catch()` at the end of a chain to perform error handling. '''Most promise chains should have a catch block to avoid dropping errors'''. +* To reject a promise, throw an `Error` instance or call the `reject` callback with an `Error` instance. +* A `.catch()` block is considered resolved if it does not re-throw an `Error` instance. Re-throw if you want to log an error message and allow other parts of a chain (i.e, an API client) to handle an error condition. +* Don't directly pass a promise's `resolve` function to `Object.addEventListener`, as it will leak the promise if the event never fires. Instead, use a single-fire `WI.EventListener` object defined outside of the promise chain and connect it inside a `.then()` body. Inside the `.catch` block, disconnect the `EventListener` if necessary. +* For APIs that return promises, document what the fulfilled value will be, if any. Example: `createSession() // --> (sessionId)` + +## Arrow Functions + +Arrow functions simplify a common use of anonymous functions by providing a shorter syntax, lexical binding of `this` and `arguments`, and implicit return. While this new syntax enables new levels of terse code, we must take care to keep our code readable. + +### Implicit return + +Arrow functions with one expression have an implicit return. All of these are equivalent (modulo `this` binding, arguments, constructor usage, etc.): + +``` +1 let foo = val => val; +2 let foo = (val) => val +3 let foo = (val) => val; +4 let foo = (val) => { return value++; }; +5 let foo = (val) => { + return value++; + }; +6 let foo = function doStuff(val) { return value++; }; +7 let foo = function doStuff(val) { + return value++; + }; +``` + +Never use option (1), because it is a special case that only applies when the function has one argument, reducing predictability. + +In cases where the return value is used and the single expression is a constant ("foo"), a variable (foo), a member (this.foo), or evaluates to a Promise, use option (2). Never use braces though, because implicit return only works if there are no braces around the single expression. + +In cases where the expression computes a value (a + 42) or performs a side effect (++a), prefer option 5. +In some sense, curly braces are a signpost to the effect of "careful, we do actual work here". + +If the implicit return is not used (4, 5, 6, 7), always put the function body on new lines from the `{` and `}` (as demonstrated in 5 and 7). + +GOOD: + +``` +setTimeout(() => { + testRunner.notifyDone(); +}, 0); +``` + +BAD: + +``` +// return value not implicitly returned + +setTimeout(() => { + testRunner.notifyDone() +}, 0); +``` + + +``` +// implicit return value not used + +setTimeout(() => testRunner.notifyDone(), 0); +``` + +### When not to arrow + +When assigning a function to a subclass prototype (in the old way of setting up classes), always use the normal function syntax, to avoid breaking subclasses who use a different 'this' binding. Note that arrow functions are NOT acceptable for assigning functions to singleton objects like `WI`, since the captured lexical `this` is typically the global object. + +GOOD: + +``` +Base.prototype.compute = function(a, b, c) { + // ... +}; + +Foo.prototype.compute = function(a, b, c) { + Base.prototype.compute.call(this, a, b, c); +}; + +WI.UIString = function(format, args) { + // ... +}; +``` + +BAD: + +``` +// `this` will be `window` + +Base.prototype.compute = (a, b, c) => { + // ... +}; + +Foo.prototype.compute = (a, b, c) => { + Base.prototype.compute.call(this, a, b, c); +}; + +WI.UIString = (format, args) => { + // ... +}; +``` + +Also use the normal function syntax when naming an anonymous function improves readability of the code. In this case, use Function.prototype.bind or assign the arrow function into a local variable first. + +GOOD: + +``` +Promise.resolve().then( + function resolved(value) { ... }, + function rejected(value) { ... } +); +``` + +BAD: + +``` +Promise.resolve().then( + (value) => { ... }, + (value) => { ... } +); +``` + + +## New class skeleton + +New Inspector object classes use ES6 class syntax and should have the following format: + +``` +WI.NewObjectType = class NewObjectType extends WI.Object +{ + constructor(type, param) + { + console.assert(param instanceof WI.ExpectedType); + + super(); + + this._type = type; + this._propertyName = param; + } + + // Static + + static computeBestWidth(things) + { + // ... + return 3.14159; + } + + // Public + + get type() { return this._type; } + + get propertyName() + { + return this._propertyName; + } + + set propertyName(value) + { + this._propertyName = value; + this.dispatchEventToListeners(WI.NewObjectType.Event.PropertyWasChanged); + } + + publicMethod() + { + /* public methods called outside the class */ + } + + // Protected + + protectedMethod(event) + { + /* delegate methods and overrides */ + } + + // Private + + _privateMethod() + { + /* private methods are underscore prefixed */ + } +}; + +WI.NewObjectType.Event = { + PropertyWasChanged: "new-object-type-property-was-changed", +}; + +``` + +## CSS + +### z-index + +Z-index variables are defined in [Variables.css](https://trac.webkit.org/browser/trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css). Usage example: + +``` +.popover { + z-index: var(--z-index-popover); +} +``` + +Read more about the rationale in [Bug 151978](https://bugs.webkit.org/show_bug.cgi?id=151978). diff --git a/docs/Deep Dive/Web Inspector/WebInspectorDebugging.md b/docs/Deep Dive/Web Inspector/WebInspectorDebugging.md new file mode 100644 index 00000000..a351959e --- /dev/null +++ b/docs/Deep Dive/Web Inspector/WebInspectorDebugging.md @@ -0,0 +1,109 @@ +# Debugging the Web Inspector + +This page contains tips and suggested workflows for isolating, understanding, and fixing code in the Web Inspector, particularly in the user interface. + +## Inspecting the Inspector + +For the Mac port, set the following defaults to allow inspecting a '''local''' Web Inspector. + +``` +defaults write com.apple.Safari WebKitDeveloperExtrasEnabled -bool YES +defaults write com.apple.Safari WebKitDebugDeveloperExtrasEnabled -bool YES +``` + +NOTE: You may need to first give Terminal Full Disk Access. Remember to turn this off afterwards. + +``` +System Preferences > Security & Privacy > Privacy give Terminal "Full Disk Access" +``` + + +## Rebuilding When Files Change + +The Web Inspector interface is loaded from the build directory (./WebKitBuild/), not the source tree (./Source/WebInspectorUI/). +Its code is not compiled like other parts of WebKit, but it is processed by scripts that copy its resources to the build directory. +Thus, to see changes you've made to Web Inspector's JS, CSS, or images, you must re-run the inspector build scripts. This can be done without recompiling all of WebKit by running the following: + +``` +make -C Source/WebInspectorUI/ release +``` + + +To automate this step, you can connect the above command to `entr`. +The `entr(1)` tool (http://entrproject.org/) can perform an action when it detects that files have changed. +The following command will run indefinitely, invoking the inspector's build scripts whenever any interface files change. + +``` +find -E Source/WebInspectorUI/ -regex ".*\.(js|css|html|svg|png)" | entr make -C Source/WebInspectorUI/ release +``` + +Then, you can open and close the inspector (or reload with Cmd+R) to see the new changes. + +NOTE: depending on your system configuration, you may need to adjust the maximum open files limit for entr to work in this case. There are approximately 1000 inspector files, so this can be fixed with the following: + +``` +ulimit -n 2048 +``` + +## Using Logging inside WebInspectorUI + +To log console messages from the inspected page and inspector pages to the system console, set the following preferences. + +``` +defaults write com.apple.Safari "com.apple.Safari.ContentGroupPageIdentifier.WebKit2LogsPageMessagesToSystemConsoleEnabled" -bool YES +defaults write com.apple.Safari WebKitLogsPageMessagesToSystemConsoleEnabled -bool YES +defaults write com.apple.Safari WebKitDebugLogsPageMessagesToSystemConsoleEnabled -bool YES +``` + +Using `console.log` and friends in the inspector interface's code will log messages in the next-level inspector. +However, both will be interleaved if you enable output to the system console as above. + +# Tips for Debugging Tests + +## Force Synchronous TestHarness Output + +Setting `InspectorTest.debug()` will log all inspector protocol traffic and `console.log` output to stderr which can be observed when the test completes or times out. + +Setting `InspectorTest.forceDebugLogging = true` will force all test output to be emitted via window.alert, which in a LayoutTest will add a message to test output without modifying the test page. + +This is useful if you suspect problems in the test harness itself, or if the test crashes before writing buffered output into the test page (which is usually scraped to produce the test output). + +## Logging to System Console/stderr While Running Tests + +This is basically the same as above, except that the defaults domain is different. Since the test executable WebKitTestRunner resets its domain defaults on every run, you must set logging defaults globally. This is not recommended for other purposes since it may cause unrelated WebKit instances to log lots of messages. + +``` +defaults write -g "com.apple.Safari.ContentGroupPageIdentifier.WebKit2LogsPageMessagesToSystemConsoleEnabled" -bool YES +defaults write -g WebKitLogsPageMessagesToSystemConsoleEnabled -bool YES +defaults write -g WebKitDebugLogsPageMessagesToSystemConsoleEnabled -bool YES +``` + +## Disabling Minification and Concatenation + +By default, all Inspector resources are combined in a single file to minimize the time spent loading many small local files through WebKit's loading infrastructure. Unfortunately, this can make stack traces in test output hard to read. To disable combining of test resources: + +### On Mac + +Go to the file: + +``` +./OpenSource/Source/WebInspectorUI/Configurations/DebugRelease.xcconfig +``` + +and set `COMBINE_TEST_RESOURCES = NO`. Then rebuild the WebInspectorUI project: + +``` +make -C OpenSource/Source/WebInspectorUI/ release +``` + +and run your test again. + +### On Linux GTK + +Add `COMBINE_TEST_RESOURCES=NO` to `--cmakeargs`. In Debug build inspector resources are not combined by default, if you want to run Release binary but disable combining of inspector UI resources add `COMBINE_INSPECTOR_RESOURCES=NO`. Build WebKit: + +``` +build-webkit --gtk --cmakeargs="-DCOMBINE_INSPECTOR_RESOURCES=NO -DCOMBINE_TEST_RESOURCES=NO" +``` + +and run your test again. diff --git a/docs/Deep Dive/Web Inspector/WebInspectorTests.md b/docs/Deep Dive/Web Inspector/WebInspectorTests.md new file mode 100644 index 00000000..93b0f9b9 --- /dev/null +++ b/docs/Deep Dive/Web Inspector/WebInspectorTests.md @@ -0,0 +1,209 @@ +# Writing Web Inspector Tests + +This page describes how various parts of the Web Inspector are tested. + +----- + +## Types of Tests + +There are several types of inspector tests: + + * **Protocol Tests** exercise the **inspector backend** independently of any particular frontend. + * **Frontend Tests** exercise the **models and controllers** underlying the Web Inspector user interface. + * **Manual Tests** exercise the user interface in ways that are difficult to automate or require infrastructure that doesn't exist yet. + * **Library Tests** exercise subsystems such as pretty printing or the protocol generator in isolation from a running Web Inspector instance. + +To date, the Web Inspector has no automated tests that exercise the user interface. In practice, the Inspector UI changes frequently, so such tests tend to be brittle, and have traditionally not been worth the trouble of maintaining. + +## How Tests Execute + + Each test is an HTML file in a per-domain directory within `LayoutTests/inspector/` or `LayoutTests/http/tests/inspector/` (for tests that load files over HTTP). Some tests may additionally include external files, which are included in special `resources/` directories that are automatically excluded from the test search path. All tests must decide which test harness to use by including either `protocol-test.js` or `inspector-test.js`. + + When the test page finishes loading, it calls the `runTest()` method provided, which signals the test harness to set up a test inspector instance that inspects the test page. Each test page defines a special `test()` method, which is automatically marshalled and injected into the Inspector instance's context. Most scripts execute in the inspector's JavaScript context, and occasionally evaluate some code in the test page's context to log test results and to trigger specific inspectable behaviors. + +## Protocol Tests + +Protocol tests are appropriate for testing inspector features that require the use of a few commands and events between the backend and frontend, and do not require the inspected page to be reloaded. Protocol tests are fairly low-level, and exercise the Inspector backend independent of a particular frontend and its controllers and models. In other words, you cannot test Managers or other classes in the WebInspector namespace using a protocol test. + +The `protocol-test.js` stub creates a dummy inspector frontend by using `window.open()` from the test page, and establishes bidirectional communication with the __child__ inspector page using `window.postMessage` and a `message` event handler. The "inspector" page that is loaded into the iframe is located at `Source/WebInspectorUI/Base/TestStub.html`. The code that runs inside the Inspector frame (i.e., code within the test() method) has access to the protocol test harness, whose methods are prefixed with `ProtocolTest`. Protocol-specific methods for sending commands and awaiting on events are available in the InspectorProtocol namespace. + +## Frontend Tests + +Frontend tests exercise the functionality of models and controllers specific to WebInspectorUI (the user interface included in WebKit trunk). They use a real, headless Web Inspector frontend that persists across navigations of the inspected (test) page. + +The `inspector-test.js` stub creates a real (for WebKit2, separate process) inspector frontend. Instead of the normal Web Inspector base page (`Main.html`), it loads a smaller version (`Test.html`) which does not load Views and other code not used by tests. The code that runs inside the Inspector (i.e., code within the test() method) has access to the frontend test harness, whose methods are prefixed with `InspectorTest`. Like ordinary Web Inspector code, injected inspector code has full access to models and controllers in the `WebInspector` namespace. (However, as noted above, not all files are loaded in the test version of the Inspector. You may need to add additional files to `Test.html` when testing new code or adding inter-class dependencies.) + +## Manual Tests + +A manual test requires manual interaction with a test page to exercise specific behaviors. The test page should describe the necessary interaction steps, and the expected output/behavior. + +## Library Tests + + * Pretty printing tests: these cover behavior of our pretty-printing code, and should be converted into layout tests. + * Protocol generator tests: these test inputs to the generator are designed to detect changes in the protocol generator's code generation routines. They do not contain any assertions. To run the tests, execute `Tools/Scripts/run-inspector-generator-tests`. + * TODO: do we have other ad-hoc tests? + +----- + +## How to Write Tests + +The properties that we strive for when writing inspector tests are: + + * **consistent**: a test should be consistent between runs, and not sporadically fail or time out. + * **robust**: a test should be robust to underlying changes in data structures or other minor changes to the code being exercised. It should not require frequent adjustments. + * **high coverage**: to uncover bugs and unaddressed situations, a test should exercise as many normal and exceptional code paths as possible. + * **self-documenting**: a test should act as executable documentation for the expected and unexpected use cases or behaviors of the code being exercised. + +With these properties in mind, here are a few hints for writing good tests: + + * Use good names in the test filename (`inspector/domain/description-of-test.html`), test suite name (`Domain.descriptionOfTest`), and in each test case's name (`TestSomethingInteresting`) and description (`"This test ensures something interesting"`). + * Use `AsyncTestSuite` (documented below) to avoid common pitfalls involved in testing asynchronous code, such as when testing the result of a command sent to the inspector backend. + * Use assertions to test invariants, pre-conditions, and post-conditions. Assertions should not need to be changed unless the code under test changes in significant ways. + * Assertions should always document the expected condition in the message, usually using obligatory language such as "should be", "should contain", "should not", etc. For example, the following shows a good and bad assertion message: + +``` +InspectorTest.expectThat(fontFamilyNames.length >= 5, "Has at least 5 fonts"); // BAD! +InspectorTest.expectThat(fontFamilyNames.length >= 5, "Family names list should contain at least 5 fonts"); // GOOD! +``` + +* Use `expectThat` instead of `assert` whenever possible. The former will always add output to the test, prefixing the condition with `PASS:` or `FAIL:`; the latter only produces output when the condition evaluates to false. Why should the default be to always log? For someone trying to understand how a test works or debug a failing test, the extra output is very helpful in understanding control flow. +* Log runtime states sparingly, and only those that may help to diagnose a failing test. Logging runtime states can make tests more self-documenting at the expense of reducing robustness. For example, if a test logs source code locations, these could change (and cause the test to fail) if text is added to or removed from relevant file. It is better to assert actual output against known-good outputs. Don't dump runtime state that is machine-dependent. + +## Assertion Matchers + +``` +InspectorTest.expectThat +InspectorTest.expectFalse +InspectorTest.expectNull +InspectorTest.expectNotNull +InspectorTest.expectEqual +InspectorTest.expectNotEqual +InspectorTest.expectShallowEqual +InspectorTest.expectNotShallowEqual +InspectorTest.expectEqualWithAccuracy +InspectorTest.expectLessThan +InspectorTest.expectLessThanOrEqual +InspectorTest.expectGreaterThan +InspectorTest.expectGreaterThanOrEqual +``` + +## Important Test Fixtures + +Common to both protocol tests and frontend tests are the `TestHarness` and `TestSuite` classes. TestHarness and its subclasses (bound to the globals ProtocolTest or InspectorTest) provide basic mechanisms for logging, asserting, and starting or stopping the test. Protocol and frontend tests each have their own subclass of `TestHarness` which contains methods specific to one environment. + +`TestSuite` and its subclasses `AsyncTestSuite` and `SyncTestSuite` help us to write robust, well-documented, and fast tests. All new tests should use these classes. Each test file consists of one (or more) test suite(s). A suite consists of multiple test cases which execute sequentially in the order that they are added. If a test case fails, later test cases are skipped to avoid spurious failures caused by dependencies between test cases. Test cases are added to the suite imperatively, and then executed using the `runTestCases()` or `runTestCasesAndFinish()` methods. This allows for programmatic construction of test suites that exercise code using many different inputs. + +A `SyncTestSuite` executes its test cases synchronously, one after another in a loop. It is usually used for unit tests that do not require communication with the backend. Each test case provides a test method which takes no arguments and returns `true` or `false` to indicate test success or failure, respectively. + +An `AsyncTestSuite` executes its test cases asynchronously, one after another, by chaining together promises created for each test. Each test case provides a test method which takes two callback arguments: `resolve` and `reject`. At runtime, each test method is turned into a Promise; like a Promise, the test signals success by calling `resolve()`, and signals failure by calling `reject()` or throwing an `Error` instance. + +## How to Debug Tests + +In general, the strategies for [wiki:"WebInspectorDebugging" debugging the Web Inspector] and debugging WebCore/WebKit2 apply the same to debugging inspector tests. Sometimes, tests can be more difficult to debug because the test harness' marshalling code can be broken by incorrectly written tests or bugs in the test harness. The test stubs provide several flags that enable extra or more reliable logging for debug purposes. Flags can be set in the corresponding `Test/TestStub.html` file for all test runs, or at the top of a `test()` method to only affect one test. + +For protocol tests: + +``` +// Debug logging is synchronous on the test page. +ProtocolTest.forceDebugLogging = false; + +// Tee all TestHarness commands to stderr from within the Inspector. +ProtocolTest.dumpActivityToSystemConsole = false; + +// Best used in combination with dumpActivityToSystemConsole. +ProtocolTest.dumpInspectorProtocolMessages = false; + +// Enables all of the above. +ProtocolTest.debug(); +``` + +For frontend tests: + +``` +// Debug logging is synchronous on the test page. +InspectorTest.forceDebugLogging = false; + +// Tee all TestHarness commands to stderr from within the Inspector. +InspectorTest.dumpActivityToSystemConsole = false; + +// Best used in combination with dumpActivityToSystemConsole. +InspectorBackend.dumpInspectorProtocolMessages = false; + +// Enables all of the above. +InspectorTest.debug(); +``` + +### Attaching a Debugger to Tests with DumpRenderTree (WebKit1) + +``` +$ DYLD_FRAMEWORK_PATH=WebKitBuild/Debug lldb -- WebKitBuild/Debug/DumpRenderTree +(lldb) run LayoutTests/inspector/dom/focus.html +``` + +To run DumpRenderTree in "server mode", which the run-webkit-tests uses to run multiple tests without restarting the process, make a file called "tests-to-run.txt" with one test per line, and launch this way instead: + +``` +(lldb) process launch -i tests-to-run.txt "-" +``` + +### Attaching a Debugger to Tests with WebKitTestRunner (WebKit2) + +TODO + +# Example Test (uses inspector-test.js, AsyncTestSuite) + +``` + + +
+ + + + +Test CSS.createStyleSheet.
+ + +``` diff --git a/docs/Deep Dive/Web Inspector/WebReplayMechanics.md b/docs/Deep Dive/Web Inspector/WebReplayMechanics.md new file mode 100644 index 00000000..1091fac0 --- /dev/null +++ b/docs/Deep Dive/Web Inspector/WebReplayMechanics.md @@ -0,0 +1,252 @@ +# Web Replay Mechanics + +*Brian Burg