Four silly browser hacks - HTTP 203

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[LAUGHTER] [MUSIC PLAYING] SURMA: So-- JAKE: So. SURMA: It's been a while. JAKE: It has been a while. SURMA: It's-- ah! It's good to-- good to back. JAKE: Is it? SURMA: Eh. JAKE: Well, let me be the-- I'll be the judge of that. SURMA: OK. Well, I thought we would start by talking about-- we've built two apps. That's basically where I'm coming from. We have built two apps-- JAKE: --in our lifetime. SURMA: --in-- yes. JAKE: Yeah. SURMA: Nothing every else we built then, except this. No, in this team that we have established here-- JAKE: Little team, yep. SURMA: --we built Squoosh. JAKE: Yes. SURMA: We have talked about that about a million times. JAKE: Mm-hmm. SURMA: One, two, or three? JAKE: Mm-hmm. SURMA: And we recently built PROXX-- JAKE: Yes. SURMA: --our little Minesweeper clone, reimagined on the web, modern visuals-- all that. JAKE: Yep. SURMA: And it turns out that when you build a real app, you sometimes have to make some workarounds happen-- about the problems that happen in the odd browser that doesn't quite fit-- JAKE: The odd browser? SURMA: Yes. JAKE: Are you talking about specifically one browser? SURMA: [LAUGHS] JAKE: All the browsers are fine except the odd browser. [LAUGHTER] SURMA: That would be a great name for a browser. JAKE: Yep. we'll release the Odd Browser. SURMA: Browse safer with Odd Browser. JAKE: Or maybe it's odd version numbers. It's like the "Star Trek" films. The odd version numbers are the bad ones. SURMA: Yeah. JAKE: And the even ones are fine. SURMA: It would make a lot sense of how Chrome behaves sometimes. JAKE: Yeah. Yes, it would. [LAUGHTER] SURMA: So I thought I would go through our code. JAKE: OK. Ah, interesting. SURMA: And figure out the top four stupidest hacks that-- JAKE: Oh, so this these are all going to be from PROXX? Or-- SURMA: PROXX and Squoosh. JAKE: PROXX and Squoosh. SURMA: Yes. JAKE: All right. That does sound like a terrible kid's TV show, doesn't it? [LAUGHS] SURMA: PROXX and Squoosh. JAKE: Welcome to PROXX and Squoosh. (HIGH VOICE) Yay! (SINGING) Bop-de-bop, da-da-da, bop-de-bop. SURMA: That's pretty much how I felt looking at the code samples. JAKE: Excellent. OK. SURMA: So yeah, let's start with the first thing, which is about the Canvas OOM. JAKE: OOM. SURMA: And-- JAKE: Um-- SURMA: Um-- JAKE: Right? SURMA: You remember this, because you fixed it. JAKE: Right. SURMA: But I'm still going to explain what it was. So in PROXX, we render our game field-- the grid where the tiles are. Some might be mines, or black holes, in our case. JAKE: Yep. SURMA: Or not black holes. And we have animations running. And so we thought to make it as fast as possible, we will be using sprites that have every frame of an animation. They look like this. JAKE: Right. SURMA: And this-- JAKE: Oh, so this is the-- OK, so this is every frame of the animation. SURMA: Well, it's not-- JAKE: We draw a rectangle over the top of it, right? For the outer bounds. SURMA: Right. So we crop out one of the things that we need and put that on the tile. And so in the next frame, we just crop out the next square from this sprite sheet and put that on screen. And if that happens every frame, it looks like an animation. JAKE: Right. SURMA: And that means you don't actually have to have-- this is four or five squares that rotate independently. JAKE: Yep. SURMA: We could do it with a DOM. That would be a lot of DOM, and a lot of layers, and it would not be fast. JAKE: Yes. SURMA: So in this case, we're just copying pixels, which is really fast. JAKE: Right. So for every frame, we were just taking part of that. Is just all of them? SURMA: No. This is a quarter or half of the frames. JAKE: A quarter or half of them. And so yes, it's every frame, 60 times a second. We were essentially taking a different section of this sprite. SURMA: Yeah, and put it on screen for one tile. JAKE: Right. OK, OK. Yes, yes, yes. SURMA: And that would happen for each tile of the game field. So that you can see if that was actual DOM, that would be too expensive. So we used sprites, and-- JAKE: How many-- on screen at a time, we would have, like, 500 of these going at once. SURMA: Potentially, or a bit more. JAKE: And doing that in the DOM-- SURMA: No. Just-- JAKE: Too slow. SURMA: It's not what it was built for. It's [INAUDIBLE]. JAKE: Right. SURMA: Yes. And because we didn't know the device that you are running on-- the web can run anywhere. It can be a low-end feature phone with, like, 320 by 240 pixels. It can be my iMac 5K monitor. JAKE: Oh, show off. Just 'cause you've got an iMac 5K monitor. SURMA: Yes, I do. I do. JAKE: I've got my-- I know what this sounds like. It's terrible complaining that I have my poor 32-inch monitor at work. But it's only, like, one DPI, and you rocking with your massive, 4K-- it's more like-- SURMA: Like 5K? JAKE: 4-- 5K screen, yes. I'm on my 1K. Anyway, I'm jealous, but carry on. SURMA: Yes. It's great. JAKE: Thanks. SURMA: But basically, we could either generate one sprite sheet and just have everyone down on the same sprite sheet, which will be rather big. And we thought, that's not good. So rather, we used Canvas to generate the sprite sheet on the client side, when you load the exact resolution that this device needs. JAKE: Yeah. So we use Canvas 2D to draw these. SURMA: Yes. JAKE: But we use Canvas WebGL to put them on the screen. SURMA: Right. JAKE: Am I crimping your content? SURMA: No. That's good. JAKE: Oh, OK. SURMA: That's exactly what's happening. JAKE: Good. SURMA: So we're using Canvas and just drawing these rectangles, because that is actually what Canvas 2D is really fast at. JAKE: Yep. SURMA: And we know we can figure out what the resolution is and what the pixel density is. And so what we have is basically a function, "generate sprite." JAKE: Right. Yes. SURMA: And so we created Canvas 2D, get the context. We scale the canvas to match the device pixel ratio, because if it's a 3X screen, it should look-- JAKE: Like a 5K monitor. SURMA: Exactly. Pixelated. JAKE: OK. SURMA: And then we just go through a loop with all the frames. We move our canvas to a position where this new frame is to 0, 0. And then call this draw frame function, which just draws a frame of the animation. So we have a function that draws-- is capable of drawing every frame, always at 0, 0. JAKE: Right. SURMA: And then we have this big canvas, and we move it to draw on it. JAKE: Excellent. SURMA: And then, now we have a canvas that contains the entire sprite sheet. And we have multiple animations, so we have multiple sprite sheets. And so this is what we did, and it worked great on all phones, except-- JAKE: The odd browser. SURMA: The odd browser. JAKE: Which in this case-- SURMA: In this case, it was iOS Safari. JAKE: It was. SURMA: Which for some reason, decided, you know what? You're creating too many canvases. I'm going to kill your page. JAKE: It took us quite a while to figure out that that's what was going. SURMA: Yeah. JAKE: Like, all we saw was a full crash. SURMA: Literally saying, this page has been reloaded due to a problem. And it would do that four or five times, and then it would give up. JAKE: Yes. SURMA: Not a great experience. JAKE: Not a great experience. SURMA: But eventually, we figured out that, well, so far we have to do this-- it is probably a memory pressure thing that we were-- because all these canvases are backed by a frame bot, or the chunk of memory represents the pixels. And having too many of those, apparently, Safari is like, you know what? This is over my threshold. I'm going to kill this page. JAKE: If you get a tab in a browser that just crashes out, I would say, more than 50% of time, it's memory. SURMA: Yeah. JAKE: Right? You've either found a bug in the browser that has caused it to crash out, but the time that a browser will willfully crash out is when you've run out of memory. SURMA: Something-- yeah. JAKE: It's like, I need that memory back, else the whole system is going to-- yeah. SURMA: So the thing that we thought, how do we fix it, we-- or I guess, you were like, well, but I can load images that have the same dimensions as our canvas. So our sprite sheet was like 2,000 by 2,000 pixels, I think. JAKE: Yeah. SURMA: We can load an image on a website like that. Why is the canvas suddenly too much? JAKE: You can load loads of images like that. SURMA: And so here is our great fix. JAKE: Right. SURMA: And for having a sprite, we generate that sprite. We turn it into a blob. JAKE: Yes, we do. SURMA: We turn that blob into an image. JAKE: Yes, we did. SURMA: And we keep that image. JAKE: And then we keep the image. SURMA: And so we only ever have one canvas at a time. JAKE: Yeah. SURMA: And so Safari is now fine. JAKE: And to do this-- oh, it's really annoying, because we are having to encode as PNG. SURMA: Yep, just to decode it. JAKE: So then we can decode as PN-- SURMA: [LAUGHS] JAKE: And now-- but now, it's-- SURMA: Which surely must be more memory pressure. They're just keeping the canvas around. JAKE: Yes, but presumably the Safari on iOS must be smarter with images than it is with canvases. SURMA: Maybe. JAKE: Like the image it can put down onto disk, rather with memory or something like that, and-- SURMA: It fixed it. It now works perfectly fine. JAKE: Oh, it was so annoying And it was purely just by finding something that we knew-- something that was similar that the browser could do. You can still write images to a canvas. You can write canvases or images to canvases, but canvases, it crashed. Images, it didn't. SURMA: OK. JAKE: Fine. SURMA: Told you, this is going to be stupid hacks. JAKE: Carry on. Yes. OK. SURMA: So the next one is-- JAKE: Worker Murder. SURMA: --is the Worker Murder. Because sometimes, workers are allowed to die. JAKE: [LAUGHS] SURMA: Sometimes, they are not. JAKE: OK. SURMA: And it still happens. So we-- both Squoosh and PROXX have off-main thread architecture going-- JAKE: Yes, they do. SURMA: Like, they do work off main thread. JAKE: Yes. SURMA: We didn't run into this problem with Squoosh, and we will figure out why throughout this. JAKE: Oh. SURMA: So what we do is usually, server worker, and we often use Comlink to make the interaction with the worker really nice. But for some reason, when we did this, our app would stop working. Just not react anymore. JAKE: In the odd browser. SURMA: In the odd browser-- in iOS Safari. JAKE: Which was iOS Safari. And we should say, many browsers have bugs, right? It just so happened that we encountered most of them on iOS Safari. SURMA: Yeah. JAKE: Not on Safari desktop. SURMA: Also-- No, only iOS Safari. JAKE: Only iOS Safari. Right. SURMA: Also-- yeah. The reason I picked these is because the solutions are so stupid. Like-- right? JAKE: Yes. SURMA: We have bugs in other browsers, but their solution is like, oh, they don't support blah. Just do dash [INAUDIBLE]. JAKE: Put an if around it. SURMA: --or something. But here, we actually had to be creative and come up with very weird solutions. JAKE: Yes. SURMA: So basically, what we figured out is that the worker got killed. JAKE: Just got killed, didn't it? SURMA: Even though it still existed and had things to do, it was basically waiting for a message. And then it's spec compliant to wait for a message. But iOS was like, you know what? The workers are expensive. We want that memory back. And [POPS LIPS] JAKE: So then, we were kind of like, well, what is this-- well, we wanted to confirm our theory. SURMA: Yeah. JAKE: Is this worker being killed? And so what if it was like, well, let's do things like, we'll ping the worker. So we can see those messages coming back. Here it is. [LAUGHTER] I see. SURMA: So we did that. JAKE: Yes. SURMA: And it made it better. JAKE: It made it better. SURMA: But it didn't quite solve it. So basically, sending a message was like, oh, it seems to be around now, but then on a more constrained iOS device, it would still get killed. And then we're like, you know what? We're just going to send a message every three seconds, because you know-- JAKE: Yes, we did. SURMA: --and we did. And that made it even better, but we still found corner cases where-- JAKE: Well, we settled on this fix for a long time. SURMA: Yeah. JAKE: This was our fix, because-- and it was really just as we were trying to debug it-- this trying to debug it made it kind of work again. And we thought, ah. No one touch it. [LAUGHTER] Walk-- SURMA: This is OK. JAKE: Walk away slowly. SURMA: [LAUGHS] JAKE: That's our solution. Fine. SURMA: And then we still, eventually found some corner cases where it would not work. The worker would still disappear. JAKE: Yep. SURMA: And-- JAKE: Well, our theory was it was disappearing. We could never fully confirm it. SURMA: No. It-- yeah. Because the initial problem is that the simulator-- the iOS simulator is very slow, and it's actually behaving differently from an actual device in terms of how iOS Safari behaves. JAKE: Yes. SURMA: And on earlier devices, we didn't have proper [INAUDIBLE] forwarding, and so we always had to deploy it. And that slowed down the entire debugging process. It was cumbersome, to say the least. JAKE: It always-- debugging this, every time we saw the bug again it felt like, during the process of debugging it, it would start working again. SURMA: Yeah. JAKE: And we'd be like, right. Let's just-- let's move away. And then it would fall over again when we weren't looking. SURMA: And then we actually found something that seemed to resolve it. We still think it has resolved it, and it's so stupid that I kind of love it. Instead of pinging it every three seconds-- JAKE: [HUMMING FANFARE] SURMA: We just put it on the global. JAKE: Yeah, we did. And again, this was me trying to debug it. And I thought, well, if I can put it on a global, then I can just debug the console. SURMA: You can de-console it. JAKE: And-- SURMA: But then it disappeared. JAKE: And then I was like, I couldn't-- I was so angry. It was like, I can't recreate the problem. And it wasn't till, hang on. Let me just remove that one line I added. Bug comes back. SURMA: To be fair, we did try to make some reduced test cases, and it never happened. So our theory is, because Comlink uses-- JAKE: Proxies. SURMA: --proxies. That the combination of sending workers when something is done by a proxy, that there's something about that garbage collection marking algorithm that might just be a weird corner case bug, which is why the workers get collected when you use Comlink. JAKE: Yeah. It thinks, because it's going through a proxy, it's not counting that as a reference when it should. SURMA: Maybe. That's a hunch. But yeah, it could-- JAKE: We're only guessing. Brilliant. SURMA: But putting in a global, that solved it. JAKE: Ta-da. SURMA: It looks even worse in our code, because we are using TypeScript. So of course, we have to ensure type. But this is actually OK? JAKE: Yeah, yeah. SURMA: It's great. JAKE: Chill. TypeScript, chill. We know we're doing the bad thing. SURMA: We know what we're doing. JAKE: Yeah. SURMA: Number three-- I thought it was quite interesting. JAKE: Oh, yes. SURMA: It's event inheritance. JAKE: I know what this one is. SURMA: So in Polymer 0.5 and 1.0 time, everything was around custom events. JAKE: Yes. SURMA: CustomEvent class, and putting your data in there and bubbling up. CustomEvent since has been kind of deprecated. It's still around. I don't think it's implemented everywhere, actually. JAKE: Well, CustomEvent is weird, isn't it? Because when you do New CustomEvent, you get an object to put on whatever you want. SURMA: Yeah. JAKE: Which a lot of stuff is moving away from these "whatever you want" objects, especially if you're using things like TypeScript. You want to use an event of a type. SURMA: Right. And so what you usually end up doing is you build your own event that extends an event. JAKE: That looks all fine to me. Yeah. SURMA: So same arguments. Now you can just put stuff-- whatever you want to on the event in a dictionary and grab the things that you want. And now you can dispatch that event, like any normal event. JAKE: Yep. SURMA: Great. Turns out, though, that in Edge and iOS Safari-- Edge is also-- JAKE: Yep. It's all Safari. SURMA: All Safari this time, not just iOS. JAKE: And Edge. Yep. SURMA: This extend event turns out to not actually be inheriting from events. JAKE: It doesn't, does it? SURMA: No. JAKE: Yep. SURMA: And so in Squoosh, we had a very simple fix. We basically just fixed it. After we have an instance created with New, we just threw it into another function that just fixed the prototype. JAKE: Fixed-- yes. SURMA: Just say, this is your prototype. This is-- you are actually in Event. JAKE: Yep. Swap that over. This bug-- the behavior-- it comes from a very weird feature of JavaScript, where a constructor can return a value. SURMA: Right. JAKE: Don't say "right" like that's normal. SURMA: Right, question mark, dot dot dot. JAKE: OK, thank you. [LAUGHTER] Well, a constructive function-- so you can return, like-- SURMA: Four. JAKE: Yes. SURMA: [LAUGHS] JAKE: Or any number of objects or something. And so when you do New Thing, you actually get a completely different object back. SURMA: Yeah. JAKE: And JavaScript is like-- SURMA: [LAUGHS] JAKE: Why not? Why not? Do what you want. And we think that's what the implementations in Edge and Safari are doing into-- SURMA: Right. OK. JAKE: That's my theory, anyway. SURMA: Well, so we used our own events in PROXX as well. JAKE: Yes. SURMA: And so I just ripped this out of Squoosh, put it in PROXX, and it still didn't work. JAKE: What? SURMA: Because it turns out that in PROXX, we're compiling to ES5. JAKE: Oh! SURMA: Because we are targeting Firefox 48 and older browsers, and so we needed ES5. And the way that both TypeScript and also Babel, I guess, transpile classes to functions with prototypes isn't quite compatible with how the browsers expect the new event constructor to work. JAKE: Oh! SURMA: So it literally just panics at construction. JAKE: I see. SURMA: So what we did instead in PROXX, is we just rolled our own factory functions. JAKE: State change event factory. Brilliant. SURMA: So instead of saying, creating a proper state change event function or class, we just wrote a function that creates a new event and then changes the prototype. JAKE: Right. So we went from doing this horrible hack for just Edge and Safari to doing it for everyone. SURMA: Right. JAKE: Brilliant. Fine. SURMA: It still works, but it's just a function that exists, and it wasn't necessary, because ES5-- JAKE: That is-- I think that's-- it's on the developer coat of arms, isn't it? "But it works." SURMA: Yeah, pretty much. JAKE: It's fine. SURMA: Pretty much. JAKE: Excellent. SURMA: I tried, I think, for an hour to somehow actually have a proper ES5 constructor function that is a proper event. I couldn't get it to work. JAKE: I-- yeah. SURMA: I just couldn't get it to work. And so I was like, all right. You know what? Factory function. Just one place where [INAUDIBLE] you just only have one custom event, I think. JAKE: Mm-hmm. SURMA: Don't care enough. JAKE: Job done. SURMA: Right. JAKE: Fine. OK. SURMA: And for the grand finale, we have the hide-y address bar. JAKE: The hide-y address bar. Excellent. OK. SURMA: Everybody probably knows that if you are on like a blog website with your mobile browser and you scroll down, the address bar-- SURMA: Hides. JAKE: Hide-y. Yeah. SURMA: Hide-y. JAKE: It's hiding. SURMA: And you scroll back up, it comes back into view. JAKE: Come back-y. SURMA: The problem with that is that different browsers seem to be behaving differently-- what 100% height or what 100 vh means in this context. JAKE: Yes. SURMA: Some browsers seem to think the address box is overlay. Some don't. Sometimes only the vh for vh is on overlay, but the percent it is. And then there is web views, which again behave differently on both Android and iOS. And we ran into this with our game, because we wanted to use natural scrolling. So our game field is actually the actual site content, and the bars are just fixed position. So it was using normal browser scrolling. So that means if you scroll down, [INAUDIBLE] will hide. Everything would re-jiggle and jank, and that was stupid. JAKE: Yeah. SURMA: Then you scroll back up. Everything came back into view. And so we thought, you know what? We're going to disable the hide-y address bar. JAKE: So the differences across browsers at this point-- I think iOS and Chrome have made a conscious decision to try and do the same thing as each other. But iOS and Chrome both do different things with different bits of CSS. So-- ah. I'm trying to remember-- SURMA: Ah! JAKE: Ah! Bah! Bah! SURMA: [LAUGHS] JAKE: I'm trying to remember off the top of my head. It's 100 vh-- means a particular thing, unless the element is position fixed. In which case, it means a different thing. And that is-- position fixed changes the rules, and that's what we were seeing. So we had the bottom bar that was position fixed, and that was kind of like-- SURMA: Oh, it was jumping around. JAKE: Jumping around. SURMA: Either way, it was annoying. JAKE: It was annoying. Yeah. SURMA: And so we thought the easiest way to fix this is just to do scrolling on an element. JAKE: Yeah. We abandoned the full page natural scrolling thing. SURMA: So what we did, we used our main element, which is where the game field was in. Or our entire UI was in, basically. Just [INAUDIBLE] made it the size of the screen, overflow auto, boom. Scrolling. JAKE: [GROWLS] SURMA: I know. JAKE: I hate this. I hate that it's 2019, and we still have to do a special thing to make scrolling work properly, and it's-- SURMA: It's the web. JAKE: OK. [SIGHS] SURMA: It's homely. JAKE: Right. Yes. SURMA: And so this-- JAKE: This is a bit new, isn't it? I didn't know much about this until we did it. SURMA: Yeah. This is basically saying to overscroll. JAKE: Yes. SURMA: The thing where when you scroll, you're at the top and try to scroll further top, it bends it down a little bit or snaps back into view. JAKE: Like a shadow or something. SURMA: Which in this case, we didn't want. So we [INAUDIBLE]. JAKE: Yes. SURMA: But it's more about-- it's a screen-filling element that can scroll, and it fixed it in iOS Safari, I think. JAKE: Mm-hmm. SURMA: But Chrome, the odd browser-- JAKE: Oh. SURMA: --was like, you know what? This looks like you're trying-- JAKE: I'd forgotten about this. I blocked this out. SURMA: [LAUGHS] Chrome was like, I think you're trying to do a page scroller, so I'm going to give you our great hide-y address bar behavior for free. JAKE: It did. Oh, of course it did. I really had blocked this out. You've gone through all the commits then, where we've just gone through-- SURMA: No. I remembered this. JAKE: You actually remembered this. SURMA: Yeah. It's first one I put in. JAKE: Oh, brilliant. OK. Yeah. SURMA: And if you remember our fix, you know how great it is. JAKE: Yes, I do. SURMA: We looked at this line-- JAKE: Yep, I did. SURMA: And we said, we can spare a pixel. JAKE: There we go. [LAUGHTER] SURMA: So this was clearly Chrome trying to be helpful-- trying to do the right thing. JAKE: And failing. It was-- SURMA: It made us so angry. JAKE: Yes. SURMA: Because we still got that address bar hide-y janky jumpy thing, and we didn't want it at all. JAKE: Yes. It's like, we're doing this so you don't do that. SURMA: So there must be a heuristic somewhere in our code that says, if you're position [INAUDIBLE] fills the entire screen, do the [INAUDIBLE] hide-y bar attached to this scroller. You know, the root scroller. JAKE: Yes. SURMA: And so we added the pixel. JAKE: We added the pixel, and that was enough. Oh, my-- oh. Ah. SURMA: I like it. It's still PTSD going on here-- JAKE: I am-- SURMA: --from the weird, weird workarounds that we have encountered-- JAKE: Yes. SURMA: --during-- JAKE: I'd forgotten about that. We should file a bug for that, because-- or we should have a way to avoid that. So that's four stupid hacks to fix four different bro-- is it four browsers? SURMA: Two or three. JAKE: Three browsers. And-- yeah. SURMA: It's really just a web developer life. This is, I feel like what, if you're trying to get something out in time, you have to make these stupid shortcuts. JAKE: You've got the correct path, but-- SURMA: You have the fast path. JAKE: The road's out. SURMA: [LAUGHS] JAKE: And you've got to-- (MOANING) blah. SURMA: Yep. JAKE: That's web development. That (MOANING) blah. SURMA: Bottom one pixel. That's what that is. [MUSIC PLAYING] Yes. JAKE: Because that was stupid. SURMA: Right. JAKE: That's what I'm saying. SURMA: That's a good ending for this episode. JAKE: [LAUGHS] SURMA: Stupid. JAKE: Four stupid things by four stupid browsers. Actually, we're only blaming two browsers, isn't it?
Info
Channel: Google Chrome Developers
Views: 34,101
Rating: 4.9140987 out of 5
Keywords: Chrome, Developers, Google, Web, GDS: Yes;
Id: ViyTYEv9dM8
Channel Id: undefined
Length: 21min 1sec (1261 seconds)
Published: Tue Jun 11 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.