Ben: So, I'm going to talk today about
the Chrome UI framework that we use on Windows
called views,
how we build UI there, how we test it,
and stuff like that.
And some more people's coming in.
[clears throat]
So anyway, hopefully, this will sort of give you
a broad idea of how the framework works.
If you haven't used it before,
if you find yourself having to do any UI development--
Maybe if you've even already been using--
done all of the UI stuff, but don't think that
the UI framework extends beyond the status bubble.
That was a joke for Glenn.
But anyway, the view system is basically--
it's a rendering system and an event handling system
for drawing stuff into Windows and handling events,
and stuff that happen in them.
So basically, you have top-level windows,
and within them you have individual controls.
And these things are-- we call them views.
And there's a base class for all of them that is view,
in the view's namespace--
in the sky, he can paint into a canvas,
he can receive events,
and do all kinds of things in response to those.
So the views toolkit provides some basic widgets
that allow you to construct UI.
And all of these widgets are just sub-classes of view.
And they override things like Paint and other stuff
to do interesting things.
So the view itself-- all of the rendering
that we do in views is use Skia.
And so when we paint, we paint into Skia canvases
and use some of the Skia utilities to make things happen.
And so, if you've done work on WebKit,
you might find that this is somewhat similar to the way
rendering works within WebKit, except much, much simpler,
because we don't have to, you know, support things like
HTML, CSS, and all that kind of stuff.
So basically, it's just a very simple layout
and rendering system.
And so, I'll just tell you a little bit about how it all
sort of fits together.
Like I said, we have this view thing
that is like the individual controls within a window.
The actual window itself at the top level--
we have something that we call a widget.
Which is-- on Windows, this is a hWnd.
Because in Windows,
eventually you have to have an hWnd.
hWnd is what Windows thinks of as the control.
And it doesn't know anything else about
what else we might do.
So, we have this class called Widget Win, on Windows,
which basically is responsible
for translating operating system messages,
things like WMPaint,
WML button down, all that sort of thing,
into things that our view system understands.
And so this guy has all these message handlers,
and he does that.
And the widget is responsible
for hosting a special kind of view called a root view.
Maybe if I just come over here and I'll just draw the structure
on this white board.
Oh, maybe Ian can--
So, Ian's just gonna turn the camera around.
Okay.
Here we go.
So I've just drawn some boxes here.
The outermost box is the Widget Win.
So there would be, you know,
when you're typically writing Windows code,
you have your hWnd and you might have child hWnds
and stuff like that.
Here is the Widget Windows, the hWnd.
And inside that, we have a single view,
which is a root view.
And the root view is a sub-class of view that does some--
basically, it figures out how to propagate events
and all that sort of thing.
So that sits there.
And then within the root view,
there is basically the view hierarchy.
And so for the Chrome browser window,
there is a fairly deep hierarchy of views,
starting with the frame, with the window
and all the way down to individual buttons
and so on the toolbar.
And then dialogue boxes, you end up with the same thing.
You know, there's the frame with the dialogue box,
and then you'll end up with individual buttons
and text fields and whatnot inside that.
And so one of the most common things that you will do
if you have to build some UI,
you might have to create a dialogue box for something.
Maybe you're implementing a new feature
and you need to configure it or pop up something.
And so, we have a special kind of widget
that is just for creating a top-level window
or dialogue box, and that's called Window.
And it's basically a sub-class of widget.
And that provides you with the ability to create
a top-level window,
and have all of the right appearance,
the Chrome blue frame and all sorts of other things
that sort of come automatically.
And so you do that in your code.
Basically, you create one of these windows,
and you provide it with something called
a window delegate.
And a window delegate is this object.
It's an interface that you implement on your object,
then you get notified of things like the window is closing,
and you also get to configure the window through
your implementation of this interface.
Things like whether or not you can be resized or maximized,
or things like that.
So by doing that, you basically--
you tell the window how it should look
when it gets created, and also,
you can respond to events that come from the window,
through your window delegate.
And typically, the way this works is--
when you build a dialogue box, you'll create a view
that is the contents of your dialogue box.
And that will actually be the thing
that sits in here somewhere.
And your view that-- your contents view
is actually your--in most cases in the Chrome code,
it ends up being the window delegate.
And that is able to respond to the, you know,
closing of the things, and it can go and configure--
it can go and, you know, do things, you know,
when the window is closed.
Maybe you wanna perform an action or something like that,
or scrape the state out of any text boxes that you have,
and things like that.
Specifically for a dialogue box,
we have some sugar on top of a window delegate called
dialogue delegate that supports being able
to create dialogue boxes
that have okay cancel buttons and such.
And so that makes that sort of thing a bit easier.
By the way, all this code--
if I didn't mention this before, I probably should have--
is in the Chrome source under source/chrome/views.
And there's some sub-directories under there
that kind of organize this into related sections.
So some of the stuff that I was just talking about,
window, window delegate, etcetera, is in view/window.
And there's a lot of the other things around there.
And there's a controls directory as well,
where you can find the individual controls
that you can use buttons, labels, etcetera, etcetera.
So I'll just mention a couple of the controls.
I mean, most of them will make sense
if you've ever done any kind of UI programming
with any framework.
You know, most of them make sense.
We have a couple of interesting ones.
One is called hWnd View.
Sometimes you need to wrap a native windows control.
Sometimes you just have an hWnd.
And you need to put that
into your view hierarchy somewhere.
And thing is, is that because an hWnd isn't a Chrome view--
it is an hWnd.
It's not gonna get resized automatically
per the layout rules
that you specify in your view hierarchy.
So what we have is a control called hWnd View
that basically allows you to take any hWnd
that you might have-- say you have a--
I'll just use one example of a renderer
or some other control that you might have,
where you just have the hWnd of it.
You can create an hWnd view and attach the hWnd to it.
And from then on,
any time the hWnd view gets resized,
it'll automatically size the hWnd with it.
So that means that you can basically have your hWnd,
your individual native hWnds take part in the window layout.
And we have some other ones like hWnd HTML view,
which is a specialization of hWnd View
that allows you to specifically embed a web rendering area
within your view hierarchy.
Now I just talked a little bit about layout,
and that's something that's kind of interesting.
A lot of the times when you're building a dialogue box,
you might use one of the--
you might use the existing layout manager,
like GridLayout, which allows you to specify--
so you have a nicely lined up arrangement of controls,
if you're putting something together like that.
And there's some header files around that define,
you know, nicely--
the nice aesthetic spacing between controls
and stuff like that.
Sometimes you might be doing something more specific,
more custom, things like the top-level browser window,
for instance, is not using a grid layout
because everything is very specific to the individual case.
In this case for your view method,
you just override layout,
and then you get to specify each child's bounds
when your layout method is called.
Layout is called automatically by the framework
whenever the top-level widget is resized.
Basically, it goes down through the hierarchy calling layout.
And then basically each child view--
each view has a layout called,
and then it goes and sizes its children.
And when the children's bounds change,
it calls layout and it just propagates like that.
So part of that--
there's another method that you might override
sometimes called Get Preferred Size.
And the preferred size of your widget
is basically the size that you want your view to be
when it's layed out.
So you might say that your view, wants to be, you know, 20 x 30,
and then when that view's parent is called,
it might call Get Preferred Size
and then size that and position it,
and stuff like that.
So that's something else that you might find you do.
I just figured I'd talk briefly about the construction
of the top-level window as well.
Sometimes you see some of these things in your call stack.
It might be interesting to know what they are.
Like I said before,
and I drew my diagram of it here,
the top-level Widget Win window is the container,
the native hWnd that contains the view hierarchy.
Top-level view is a root view.
Then inside that, in every single case these days,
the only child of the root view
is something called a non-client view.
And this kind of maps to the window's concept
of client and non-client rendering.
On Windows, when you have a non-client area,
this is the area that is the frame of the window,
the title by the sizing border, stuff like that.
And then inside that, you have a client area,
which is the contents of the window.
So it kind of replicated that here in the view system.
The non-client view contains the client view,
and it also contains a-- what we call a frame view,
which actually renders
the blue border around the window.
And so-- and that can do
all of the specific functionality for that.
And then the client view is the thing
that actually contains the contents.
So I should have simplified this a bit before
when I was talking about dialogue boxes,
and I said that you would make a contents view for your--
had the controls of your dialogue box,
and that would go into the window.
That actually goes inside of client view.
You don't actually get to see any of this.
It's all hidden behind the call to create Chrome window
that you make.
But the client view is the thing that basically--
in the case of a dialogue box,
the client view provides the Okay and Cancel buttons,
and stuff like that.
And you may find that, you know,
you find yourself in there sometimes
if you're trying to figure something out.
So just a general high-level comment about the way that
the front end is being developed primarily because--
all the stuff I've talked about so far
is related to UI that we've been building on windows.
But we are a cross-platform project.
And so there is a lot of logic
that is shared between platforms.
And you'll find that there are a lot of objects
within the browser front end
that are actually cross platforms.
Things like browser, tab content, stuff like that.
And so we didn't want to tie them too closely together
with our windows implementation of the front end,
so we used the sort of the standard approach
for this in model view controller layout, where--
we consider some of the state objects like browser,
tab contents, etcetera, to be the state of the model
in this case, and then the--
all of the Chrome view stuff, that's kind of the--
that's sort of the view controller side of things.
And what this means is that we can separate out this model
and we can actually use that on Mac and Linux,
which is what we're doing.
And also because there isn't this sort of type coupling
of that particular UI state.
And it's also a lot easier to write unit tests
for the model.
And I'll get to that in just a moment.
Actually, I'll get to that right now.
So because the--
like I said, because it separated it out,
it's been more possible to write more unit tests.
Sometimes you have a relatively simple state object,
and you can just write a regular unit test for it
using the existing unit test framework
and get that to work.
Sometimes you wanna do something a little bit more complicated,
and you wanna test functionality that goes out and loads pages,
you know, off a test server and stuff like that.
So we have a thing called in-process browser tests
that basically allow--
that basically spins up a message loop
and allows you to simulate running the actual browser,
but in a unit test.
And these are pretty easy to write.
We're still getting some of the kinks
worked out of the system,
but they're still pretty easy to write and run in isolation.
And finally, we have UI tests which are much higher level,
very deep sort of tests that basically run
the whole browser in a separate process.
Basically, to run the browser as a user would.
And it sends commands to it through an automation API.
I would recommend if you're writing new features
to write most stuff as unit tests
or in-process browser tests
'cause these are the easiest to debug.
But if you want to get--
if you want something to run on Chrome Bot
or any of these other things,
you would need to write UI tests, and automated UI tests.
So that's sort of a brief summary of the framework.