Surfin’ Safari

WebKit nightlies support HTML5 noreferrer link relation

Posted by Nate Chapin on Thursday, November 19th, 2009 at 10:18 am

WebKit nightlies now support the HTML5 noreferrer link relation, a neat little feature that allows web developers to prevent browsers from sending the Referrer: header when navigating either <a> or <area> elements.  Just add noreferrer in the rel attribute of a link like so:

<a href="www.example.com" rel="noreferrer">noreferrer!</a>

When example.com receives the HTTP request generated by clicking this link, the HTTP Referer field will be empty, as if the user had navigated from about:blank.

When rel=”noreferrer” is used in conjunction with target=”_blank”, WebKit will also set the window.opener attribute to null.  This usage is interpreted as a request from the web page author that any scripts on the destination page should be run in a new context, even if the destination page would normally be considered to be of the same origin.

The noreferrer link relation is one of many link relations defined in the HTML5 spec, and the first to be implemented in WebKit.  We hope it will make life a little easier for web developers who have concerns about user privacy and security.

Kenneth Rohde Christiansen is now a WebKit reviewer!

Posted by Gustavo Noronha on Wednesday, November 11th, 2009 at 10:59 am

Kenneth has been participating in WebKit development since late 2008, when he started the initial EFL port. Since early 2009 he has contributed a great deal on all fronts of the Qt port, including WebCore integration and API. He has also played an important role in making sure the GTK+ and Qt ports work together wherever possible.

Please join me in congratulating Kenneth on his reviewer status!

Web Inspector Updates

Posted by Joseph Pecoraro on Tuesday, November 3rd, 2009 at 7:13 pm

A number of exciting new features have been added to the Web Inspector since our last update. Today we would like to highlight some of those features! This post is also available in Japanese (日本語), thanks to Keishi Hattori (服部慶士).

If you would like to play with most of these features you will need to be running a recent WebKit Nightly. Once downloaded make sure that you enable the Web Inspector by checking “Show Develop menu in menu bar” under the Advanced tab in the Preferences.

Enabling the Inspector in Preferences

Editing Element Attributes and Style Properties #

Editing Element Attributes and Style Properties has been made even simpler and more developer friendly. The interfaces for editing attributes and properties now support tabbing to allow you to move between items, and create new items with ease.

Besides tabbing you can also add a new attributes to a node.  Start by hovering over the node in the Element’s Tree Hierarchy and after a polite wait a clickable template for a new attribute will appear.

Edit Element Attributes

Related Bug Reports: Add Element Attributes, Tabbing, and Improved UI.

Creating and Modifying CSS Rules and Selectors #

A powerful new feature in the Web Inspector allows you create new or modify existing CSS Rules and Selectors. We expect both developers and designers will find this very useful when experimenting with new ideas or tweaking existing designs.

The interface for working with selectors starts with a new Gear Menu in the Styles Sidebar Pane. Select “New Style Rule” and a new section will be created for you, pre-populated with an intelligent selector from the selection in the Elements Tree Hierarchy. Editing selectors is activated by double-clicking. Once again, tabbing will allow you to navigate between selectors and their properties.

When editing selectors there is visual feedback when you create or modify a selector that does not affect the selected node in the Elements Tree Hierarchy. This indicator helps detect errors when making changes.

Create and Modify CSS Selectors

One more tweak to the Styles Pane is that there is always a section for the selected node’s style attribute. This allows you to easily add style information to the node as you usually would via the Styles Pane instead of editing or creating a “style” attribute. This section is nearly always on top due to how CSS specificity works.

Related Bug Reports: Selectors Support and Move to Gear Menu.

CSS Color Representations #

Colors in the Styles Pane can be shown in any of their possible representations. For simple colors this includes short hex, full hex, rgb, hsl, and potentially a nickname. For advanced colors this includes rgba and hsla. For example the color “white” can be represented as: #FFF, #FFFFFF, rgb(255, 255, 255), hsl(0, 100%, 100%), and white.

You can use the Styles Pane’s Gear Menu to set your preferred representation. However, if you want to cycle through an individual color’s different representations you can do so by clicking on its associated color swatch.

Mutiple Color Representations

Related Bug Reports: Color Representations, Preference and Gear Menu, and UI Improvement.

DOM Storage #

The Storage Panel (formerly the Databases Panel) now allows you to monitor DOM Storage areas like localStorage and sessionStorage in a familiar datagrid. The DOM Storage datagrid displays live updates so monitoring changes is possible without manually refreshing the view.

Also, the familiar creation and editing techniques apply to the datagrid. To add a new key/value pair just double-click in any open area, or double-click an existing item to start editing. Tabbing works as you would expect.

Observing DOM Storage Key/Value Pairs

Related Bug Reports: DOM Storage Support, Live Updates, Create New Items, and Tabbing.

Keyboard Shortcuts #

Keyboard shortcuts are always desired by developers. They can be hard to discover, so here is a complete list and here are the ones that were added recently:

  • Switch PanelsCommand-[ and Command-] on a Mac or Control-[ and Control-] on other platforms.
  • Delete a Node in the Tree Hierarchy — either Delete or Backspace keys will do the trick.
  • Quick Edits in the Tree Hierarchy — Hitting Enter or Return on a Node in the Tree enters the editing mode for that type of Node. For a Text Node you will start editing the content. For Element Nodes you start editing the first attribute, or, for convenience, a new attribute will be added for you.

The Scripts Debugger was updated to support some popular keyboard shortcuts:

  • ContinueF8 or Command-/ on a Mac or Control-/ on other platforms.
  • Step OverF10 or Command-’ on a Mac or Control-’ on other platforms.
  • Step IntoF11 or Command-; on a Mac or Control-; on other platforms.
  • Step OutShift-F11 or Shift-Command-; on a Mac or Shift-Control-; on other platforms.
  • Next Call FrameControl-. on all platforms.
  • Previous Call FrameControl-, on all platforms.
  • Evaluate Selection When on a BreakpointShift-Command-E on a Mac or Shift-Control-E on other platforms.

Related Bug Reports: Switch PanelsDelete Node, Quick Edit, General Debugger Shortcuts and Evaluate Selection.

Cookies #

Viewing Cookie information is now possible under the Storage Panel. Supported platforms show all of the cookies and their hidden information for all domains accessed on the inspected page. Cookie information includes the name, value, path, expiration date, http only flag, and secure (https) flag. Supported platforms may also delete cookies.

If your platform doesn’t have full support you aren’t left in the dark. You will still be able to see the keys and values of the cookies that are accessible via JavaScript on the inspected page.

Inspect Hidden Cookie Information

Related Bug Reports: Initial Support, Hidden Data 1 and 2, Cookies for Sub-Resources, and UI Improvements

Event Listeners #

A new Sidebar Pane has been added to the Elements Panel which displays the registered Event Listeners for the selected node.  The Event Listeners that are shown for the selected node are in the exact order that they are fired through the Capturing and Bubbling phases.  This provides developers with the most accurate and useful information possible.

The user interface shows the registered Event Listeners separated by type. If a node has both “onclick” and “onmouseover” listeners then they will naturally appear in different sections. You can also set your filter preference using the Gear Menu. You can choose to see only the listeners registered on the selected node, or the entire event flow.

Inspect Registered Event Listeners

We are actively looking for UI improvements in this area. So if you have some ideas or feedback please feel free to let us know on this bug report!

Related Bug Reports: Event Listeners.

Syntax Highlighting #

Syntax highlighting enhances readability, makes debugging code easier, and looks really awesome. The Web Inspector now includes syntax highlighting for JSON and CSS.

CSS Syntax Highlighting

CSS Syntax Highlighting even works on the more complex “at-rules” such as @import, @media and @font-face. In addition to supporting the syntax highlighting in the Resources Panel, inline scripts and styles in the Elements Tree Hierarchy are syntax highlighted!

Inline JavaScript and CSS Syntax Highlighting

Related Bug Reports: JSON Highlighting, CSS Highlighting, and Inline Highlighting.

Breakpoints and Watch Expressions #

The Script Debugger continues to become more powerful and more useful.  We already mentioned the keyboard shortcuts above, but there are plenty of other enhancements.

There is a new Breakpoints Sidebar Pane that allows you to easily monitor and work with your breakpoints across all files without the hassle of searching for them.  Each sidebar entry shows the source line and contains a checkbox that allows you to directly enable or disable the breakpoint. Clicking on the entry will jump you directly to the highlighted line in the source file. Finally, deleting a breakpoint has been made easier by clicking the “blue tag” breakpoint indicator.  The tag will cycle through its three states of active, inactive, and removed.

A powerful feature added to the debugger is Conditional Breakpoints. Once you have a breakpoint set, right click on the “blue tag” breakpoint indicator and you will get a popup asking for a conditional statement for that breakpoint. Simply provide an expression and the breakpoint will only pause from then on only if the condition is true.

Breakpoints and other Debugging Improvements

Another new feature in the Debugger is Watch Expressions.  In this new Sidebar Pane you can add any number of expressions that evaluate in the global scope normally but in the local scope when paused in the debugger. Once added you get the full Object Properties tree view of the values of each expression.  These watch expressions automatically refresh when the debugger pauses. They are also persist across page loads.

Watched Expressions in Action

Related Bug Reports: Breakpoints Sidebar PaneWatch Expressions, Evaluate on Breakpoint, Conditional Breakpoints, and Delete Breakpoints.

Debugging AJAX #

An extremely valuable feature for developers working with AJAX is the ability to view the exact parameters and payload sent on XMLHttpRequests.

In the individual resource view there are new sections for viewing submitted Form Data, Query String Parameters, and Request Payloads when appropriate. You can toggle viewing the information in its unencoded (default) and encoded forms with a double-click.

There is also new section named HTTP Information which contains the Request Method (GET, POST, etc.) and the Status Code (200, 404, etc.). Additionally, it adds a colored dot next to the requested URL to show the status (green for success, orange for redirect, and red for error).

View Submitted Form Data and HTTP Information

Related Bug Reports: HTTP Status Code and Data, Parameters, and Payload

Resources and Console Scope Bars #

In order to filter through the Resources or Console messages the Web Inspector now sports some familiar Scope Bars. This has proven to be very useful in the Resources Panel for easily viewing all resources of a particular type.

Quick Filtering Scope Bars for Resources Types and Console Message Types

Related Bug Reports: Resources Scope Bar and Console Scope Bars.

Resources Timeline #

The Web Inspector now specifically shows in the timeline when the DOMContentLoaded and Load events fire. This helps clarify the time it takes for pages to load and helps you improve your websites load times.

DOMContent Ready Event and Page's Load Event Indicators

Related Bug Reports: Show Load Lines

Resources Interactivity #

A couple new features allow you to more directly access individual resources from within the Web Inspector. Instead of copying their URL and opening a new tab manually you can now double-click the Resource in the sidebar to open it directly in a new window. Or, you can drag and drop the resource using HTML5 drag and drop events!

Related Bug Reports: Open Resource Directly and Drag and Drop.

Console Improvements #

Properties in the Web Inspector’s Console are now sorted in a much more natural and useful way. By sorting keys alphanumerically Arrays with greater then 10 elements are much easier to work with.

Alphanumeric Sorting

Another tweak is that collections such as NodeLists and HTMLCollections are now displayed like Arrays.  This meaning that their contents are shown directly in the console, no longer requiring any extra boilerplate.

More Descriptive Nodelists

Related Bug Reports: Sorting and NodeLists.

Firebug Command API Improvements #

More improvements have been made to support more of the Firebug Command Line API. The Web Inspector now supports the inspect() function, which can take an Element, Database, or Storage area and automatically jumps to the appropriate Panel with information. Also, the $0-$4 variables contain the current and previous selected nodes from the Elements Tree Hierarchy.

These command line APIs are usable inside the Web Inspector’s Console. To make working with these APIs even easier, they now show up in the Console’s autocompletion.

Related Bug Reports: $# Variables, inspect() Function, and Autocompletion.

How You Can Contribute #

Many of these new features were added by members of the Open Source Community. We would like to encourage you to contribute as well! Since the Web Inspector itself is mostly HTML, JavaScript, and CSS that means that you already have the skills you need to join in! Interested? Play around right now by inspecting the inspector itself!

Work on the Web Inspector using the Web Inspector!

If you’re interested in contributing and have any questions please stop by the #webkit-inspector IRC channel! As an encouragement to developers, included at the end of each section above are the core bug reports that were involved in bringing each of these features to life.

Finally, if you have ideas for new features, any improvements, or if you’ve stumbled across a bug then please don’t hesitate to create a bug report. This link has pre-populated most of the fields so that you only need to fill out the Summary and Description. As always you should do a quick search through the existing inspector bugs first.

WebGL Now Available in WebKit Nightlies

Posted by Chris Marrin on Monday, October 19th, 2009 at 1:55 pm

Introduction

WebGL is a new standard being worked on in the Khronos consortium. The work done in Khronos is only available to its members, so I can’t show you the spec just yet. But it will become public within the next few months after a review by Khronos members. The good news is that WebGL is now available in WebKit nightlies as of October 4, 2009 (r49073). So if you’re running Leopard or Snow Leopard you can try it out for yourself. WebGL runs in the HTML Canvas element, so it works very similarly to the 2D Canvas capability currently in WebKit.

OpenGL for the Web

OpenGL has been around for ages, so it’s very mature. It can handle all the features of the most advanced graphics cards, but works across a wide variety of hardware. WebGL is based on OpenGL ES 2.0 which is a shader based API.

WebGL is a very low level API, so it’s not for the faint of heart. OpenGL’s shading language, GLSL, is itself an entire programming environment. So doing even simple things in WebGL takes a lot of code. You have to load, compile and link the shaders, setup vertex buffer objects to hold the shapes, and setup the variables to be passed into the shaders. Then you have to do matrix math to animate the shapes. If you want to learn more about all this before continuing, head over to the OpenGL Site for some nice tutorials.

Getting Started

WebGL is really cool! But because it’s new and still under development it isn’t turned on by default. To do that, you need to go into Terminal and type this:

    defaults write com.apple.Safari WebKitWebGLEnabled -bool YES

Once you’ve done that, restart the WebKit nightly build. Then click on the image below. If you see a spinning cube, you have WebGL installed and enabled. If not go back and make sure you have the latest Safari and you typed the above line correctly.


Image of Spinning Box


Click to see Spinning Box (requires WebGL)

If you don’t have a nightly build, you can still see WebGL in action here.

A Simple Example

Let’s see how to create the spinning cube above. For the examples and demos here I’ve created a couple of files of JavaScript utilities to help out: one with some general utilities and another with a set of matrix functions. These will let us focus on the different steps needed to use WebGL without worrying about the details.

Like I said, WebGL is built on top of the Canvas Element. So just like you do for a 2D Canvas you start out by getting a CanvasRenderingContext with a call to the getContext method of the Canvas Element, passing the string “webkit-3d” (this is temporary, and will eventually change to “webgl”). The returned object has a set of functions very similar to OpenGL ES 2.0.

Using Shaders

Nothing happen in WebGL without shaders. They take shape data and turn it into pixels on the screen. When using GLSL you define two separate shaders. The vertex shader runs on each corner of every triangle being rendered. Here you transform the points, pass along the texture coordinates and use the normals to compute a lighting factor based on the normals of each triangle. There is a really nice GLSL Tutorial on lighting. GLSL gives you one special variable to store the transformed corner point, gl_Position. The value stored there for each of the corners of a triangle is used to interpolate all the pixels being output. The texture coordinates and lighting factor are passed in varying variables we created for the purpose.

All these values are passed to the fragment shader, which runs on each pixel of every transformed triangle passed in. This is where you get the appropriate pixel from the texture, adjust its lighting, and output the pixel. GLSL gives you a special variable for this, gl_FragColor. Whatever color your store there will be the color of that pixel.

So let’s define the shaders. I’ll use normal script notation here, even though HTML ignores it. It’s a useful way to include GLSL. The contents of the script will be passed as a string to the shaderSource function:

<script id="vshader"type="x-shader/x-vertex">
        uniform mat4 u_modelViewProjMatrix;
        uniform mat4 u_normalMatrix;
        uniform vec3 lightDir;

        attribute vec3 vNormal;
        attribute vec4 vTexCoord;
        attribute vec4 vPosition;

        varying float v_Dot;
        varying vec2 v_texCoord;

        void main()
        {
            gl_Position = u_modelViewProjMatrix * vPosition;
            v_texCoord = vTexCoord.st;
            vec4 transNormal = u_normalMatrix * vec4(vNormal,1);
            v_Dot = max(dot(transNormal.xyz, lightDir), 0.0);
        }

</script>

<script id="fshader" type="x-shader/x-fragment">

        uniform sampler2D sampler2d;

        varying float v_Dot;
        varying vec2 v_texCoord;

        void main()
        {
            vec2 texCoord = vec2(v_texCoord.s, 1.0 - v_texCoord.t);
            vec4 color = texture2D(sampler2d,texCoord);
            color += vec4(0.1,0.1,0.1,1);
            gl_FragColor = vec4(color.xyz * v_Dot, color.a);
        }

</script>

The vertex shader in this example simply sends along the vertex position, vPosition to the fragment shader after transforming it by a composite model-view/projection matrix. We’ll get to that later. Then it passes along the texture coodinate, vTexCoord, and uses the normal in vNormal to compute a lighting factor, v_Dot for the fragment shader. The fragment shader is even simpler. It just gets a pixel from the texture, (after flipping the texture coordinate so the image is right-side up). Then multiplies that by the lighting factor passed in from the vertex shader. This causes the pixels to be brighter when a side of the cube is facing you and darker when it is at an angle, giving it a realistic lighting effect.

Initializing the Engine

Now we have to get WebGL up and running. The utility library we first loaded will help us here:

    function init()
    {
        // Initialize
        var gl = initWebGL(
                // The id of the Canvas Element
                "example1",
                // The ids of the vertex and fragment shaders
                "vshader", "fshader",
                // The vertex attribute names used by the shaders.
                // The order they appear here corresponds to their index
                // used later.
                [ "vNormal", "vTexCoord", "vPosition"],
                // The clear color and depth values
                [ 0, 0, 0, 1 ], 10000);

        // Set some uniform variables for the shaders
        gl.uniform3f(gl.getUniformLocation(gl.program, "lightDir"), 0, 0, 1);
        gl.uniform1i(gl.getUniformLocation(gl.program, "sampler2d"), 0);

        // Enable texturing
        gl.enable(gl.TEXTURE_2D);

        // Create a box. On return 'gl' contains a 'box' property with the 
        // BufferObjects containing the arrays for vertices, normals, texture 
        // coords, and indices.
        gl.box = makeBox(gl);

        // Load an image to use. Returns a CanvasTexture object
        spiritTexture = loadImageTexture(gl, "spirit.jpg");

        // Create some matrices to use later and save their locations in the shaders
        gl.mvMatrix = new CanvasMatrix4();
        gl.u_normalMatrixLoc = gl.getUniformLocation(gl.program, "u_normalMatrix");
        gl.normalMatrix = new CanvasMatrix4();
        gl.u_modelViewProjMatrixLoc =
                gl.getUniformLocation(gl.program, "u_modelViewProjMatrix");
        gl.mvpMatrix = new CanvasMatrix4();

        // Enable all the vertex arrays
        gl.enableVertexAttribArray(0);
        gl.enableVertexAttribArray(1);
        gl.enableVertexAttribArray(2);

        // Setup all the vertex attributes for vertices, normals and texCoords
        gl.bindBuffer(gl.ARRAY_BUFFER, gl.box.vertexObject);
        gl.vertexAttribPointer(2, 3, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, gl.box.normalObject);
        gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, gl.box.texCoordObject);
        gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0);

        // Bind the index array
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.box.indexObject);

        return gl;
    }

After this initialization we have the shaders loaded and attached to a GLSL program, which is how you define the interface to your shaders. You pass uniforms to a shader for values that don’t change, and vertex attributes for things that do, like vertices. Most of this is taken care of in the library, but you can pass additional values here, like we do with the lightDir and sampler2d uniforms. Here we also tell WebGL that we want to use the arrays the makeBox() function set up containing the vertices, normals and texture coordinates.

Setting Up the Viewport

Before we can render, we have to tell the canvas how to map the objects we are drawing from modeling coodinates, which is the coordinate space we defined the box in, to viewport coordinates. We do that with a transformation matrix. We will use a perspective projection which will make closer objects look larger than further ones, just like in the real world. Here we will use the matrix library we loaded:

    function reshape(gl)
    {
        var canvas = document.getElementById('example1');
        if (canvas.clientWidth == width && canvas.clientHeight == height)
            return;

        width = canvas.clientWidth;
        height = canvas.clientHeight;

        // Set the viewport and projection matrix for the scene
        gl.viewport(0, 0, width, height);
        gl.perspectiveMatrix = new CanvasMatrix4();
        gl.perspectiveMatrix.lookat(0,0,7, 0, 0, 0, 0, 1, 0);
        gl.perspectiveMatrix.perspective(30, width/height, 1, 10000);
    }

We save the perspectiveMatrix for use later. It transforms from world coordinates to viewport coordinates. We will go from modeling coordinate to world coordinates in the next step.

Drawing the Box

Now we’re all set up and we can finally draw our box. Most of the hard work is done but we still have to tell the box we want it to spin, and to do that we define a model-view matrix, which transforms from modeling coordinates to world coordinates. This tells the box where and at what angle we want it to appear. Then we multiply that by the perspective matrix we saved before to complete the transformation all the way from modeling coordinates to viewport coordinates. We also turn the model-view matrix into a normal matrix so it can be used to compute the proper lighting on the box:

    function drawPicture(gl)
    {
        // Make sure the canvas is sized correctly.
        reshape(gl);

        // Clear the canvas
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        // Make a model/view matrix.
        gl.mvMatrix.makeIdentity();
        gl.mvMatrix.rotate(currentAngle, 0,1,0);
        gl.mvMatrix.rotate(20, 1,0,0);

        // Construct the normal matrix from the model-view matrix and pass it in
        gl.normalMatrix.load(gl.mvMatrix);
        gl.normalMatrix.invert();
        gl.normalMatrix.transpose();
        gl.uniformMatrix4fv(gl.u_normalMatrixLoc, false,
                gl.normalMatrix.getAsCanvasFloatArray());

        // Construct the model-view * projection matrix and pass it in
        gl.mvpMatrix.load(gl.mvMatrix);
        gl.mvpMatrix.multRight(gl.perspectiveMatrix);
        gl.uniformMatrix4fv(gl.u_modelViewProjMatrixLoc, false,
                gl.mvpMatrix.getAsCanvasFloatArray());

        // Bind the texture to use
        gl.bindTexture(gl.TEXTURE_2D, spiritTexture);

        // Draw the cube
        gl.drawElements(gl.TRIANGLES, gl.box.numIndices, gl.UNSIGNED_BYTE, 0);

        // Finish up.
        gl.flush();

        // Show the framerate
        framerate.snapshot();

        currentAngle += incAngle;
        if (currentAngle > 360)
            currentAngle -= 360;
    }

Once this is all done you simply add a JavaScript timer to keep changing the angle and rendering the box in its new position and you have a spinning box!

Where to Next?

So, as you can see there’s a lot to learn about 3D rendering. There are some nice tutorials at the OpenGL Site. Most of these are not specific to OpenGL ES 2.0, so you’ll have to figure out what features are and are not available. Unfortunately there aren’t a lot of specific ES 2.0 examples yet. But I think these tutorials will give you a good start. There’s also a great book specifically about OpenGL ES 2.0 called the OpenGL ES 2.0 Programming Guide.

There are also a few WebGL examples in the wild already. Check them out here, here and here. WebKit has a few samples as well:

Spinning Box

Earth

Many Planets

Teapot per-vertex

Teapot per-pixel

WebGL+CSS Animation

Pavel Feldman and Dmitry Titov are now WebKit reviewers.

Posted by David Levin on Tuesday, October 13th, 2009 at 2:51 pm

Pavel has contributed some great new features for the Web Inspector and spent a lot of time on the Web Inspector to help unfork the Chromium port. Dmitry has worked on timers, workers, test improvements, and various other enhancements.

Please join me in congratulating Pavel and Dmitry on their reviewer status!