]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/abaco/page.c
abaco: fix double free race of p->status string (thanks BurnZeZ for the proc snap)
[plan9front.git] / sys / src / cmd / abaco / page.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <memdraw.h>
5 #include <thread.h>
6 #include <cursor.h>
7 #include <mouse.h>
8 #include <keyboard.h>
9 #include <frame.h>
10 #include <plumb.h>
11 #include <html.h>
12 #include "dat.h"
13 #include "fns.h"
14
15 static void pageload1(Page *, Url *, int);
16
17 static
18 void
19 addchild(Page *p, Page *c)
20 {
21         Page *t;
22
23         c->parent = p;
24         c->w = p->w;
25         c->b = p->b;
26         c->col = p->col;
27         c->row = p->row;
28         if(p->child == nil)
29                 p->child = c;
30         else{
31                 for(t=p->child; t->next!=nil; t=t->next)
32                         ;
33                 t->next = c;
34         }
35 }
36
37 static
38 void
39 loadchilds(Page *p, Kidinfo *k)
40 {
41         Runestr rs;
42         Kidinfo *t;
43         Page *c;
44
45         addrefresh(p, "loading frames...");
46         p->kidinfo = k;
47         for(t=k->kidinfos; t!=nil; t=t->next){
48                 c = emalloc(sizeof(Page));
49                 addchild(p, c);
50                 if(t->isframeset){
51                         c->url = urldup(p->url);
52                         loadchilds(c, t);
53                 }else{
54                         c->kidinfo = t;
55                         /* this check shouldn't be necessary, but... */
56                         if(t->src){
57                                 rs.r = urlcombine(p->url->act.r, t->src);
58                                 rs.nr = runestrlen(rs.r);
59                                 pageload1(c, urlalloc(&rs, nil, HGet), FALSE);
60                                 closerunestr(&rs);
61                         }
62                 }
63         }
64 }
65
66 static struct {
67         char *mime;
68         char *filter;
69 }filtertab[] = {
70         "image/gif",    "gif -t9",
71         "image/jpeg",   "jpg -t9",
72         "image/jpg",    "jpg -t9",
73         "image/pjpeg",  "jpg -t9",
74         "image/png",    "png -t9",
75         "image/ppm",    "ppm -t9",
76         nil,    nil,
77 };
78
79 char *
80 getfilter(Rune *r, int x, int y)
81 {
82         char buf[128];
83         int i;
84
85         snprint(buf, sizeof(buf), "%S", r);
86         for(i=0; filtertab[i].mime!=nil; i++)
87                 if(cistrncmp(buf, filtertab[i].mime, strlen(filtertab[i].mime)) == 0)
88                         break;
89
90         if(filtertab[i].filter == nil)
91                 return nil;
92
93         if(x==0 && y==0)
94                 return smprint("%s", filtertab[i].filter);
95         if(x!=0 && y!=0)
96                 return smprint("%s | resize -x %d -y %d", filtertab[i].filter, x, y);
97         if(x != 0)
98                 return smprint("%s | resize -x %d", filtertab[i].filter, x);
99         /* y != 0 */
100         return smprint("%s | resize -y %d", filtertab[i].filter, y);
101 }
102
103 static Cimage *cimages = nil;
104 static QLock cimagelock;
105
106 static
107 void
108 freecimage(Cimage *ci)
109 {
110         Cimage *ci1;
111
112         qlock(&cimagelock);
113         if(decref(ci) == 0){
114                 if(ci->i)
115                         freeimage(ci->i);
116                 else if(ci->mi)
117                         freememimage(ci->mi);
118                 urlfree(ci->url);
119                 ci1 = cimages;
120                 if(ci1 == ci)
121                         cimages = ci->next;
122                 else{
123                         while(ci1){
124                                 if(ci1->next == ci){
125                                         ci1->next = ci->next;
126                                         break;
127                                 }
128                                 ci1 = ci1->next;
129                         }
130                 }
131                 free(ci);
132         }
133         qunlock(&cimagelock);
134 }
135
136 static
137 void
138 closeimages(Page *p)
139 {
140         int i;
141
142         for(i=0; i<p->ncimage; i++)
143                 freecimage(p->cimage[i]);
144         free(p->cimage);
145         p->cimage =nil;
146         p->ncimage = 0;
147 }
148
149 static
150 Cimage *
151 loadimg(Rune *src, int x , int y)
152 {
153         Cimage *ci;
154         Runestr rs;
155         char *filter;
156         int fd;
157
158         ci = emalloc(sizeof(Cimage));
159         rs. r = src;
160         rs.nr = runestrlen(rs.r);
161         ci->url = urlalloc(&rs, nil, HGet);
162         fd = urlopen(ci->url);
163         if(fd < 0){
164     Err1:
165                 return ci;
166         }
167         filter = getfilter(ci->url->ctype.r, x, y);
168         if(filter == nil){
169                 werrstr("%S unsupported: %S", ci->url->ctype.r, ci->url->act.r);
170     Err2:
171                 close(fd);
172                 goto Err1;
173         }
174         fd = pipeline(fd, "%s", filter);
175         free(filter);
176         if(fd < 0)
177                 goto Err2;
178         ci->mi = readmemimage(fd);
179         close(fd);
180         if(ci->mi == nil){
181                 werrstr("can't read image");
182                 goto Err2;
183         }
184         return ci;
185 }
186
187 static
188 Cimage *
189 findimg(Rune *s)
190 {
191         Cimage *ci;
192
193         qlock(&cimagelock);
194         for(ci=cimages; ci!=nil; ci=ci->next)
195                 if(runestrcmp(ci->url->src.r, s) == 0)
196                         break;
197
198         qunlock(&cimagelock);
199         return ci;
200 }
201
202 void
203 loadimages(Page *p)
204 {
205         Cimage *ci;
206         Iimage *i;
207         Rune *src;
208
209         addrefresh(p, "loading images...");
210         reverseimages(&p->doc->images);
211         for(i=p->doc->images; i!=nil; i=i->nextimage){
212                 if(p->aborting)
213                         break;
214                 src = urlcombine(getbase(p), i->imsrc);
215                 ci = findimg(src);
216                 if(ci == nil){
217                         ci = loadimg(src, i->imwidth, i->imheight);
218                         qlock(&cimagelock);
219                         ci->next = cimages;
220                         cimages = ci;
221                         qunlock(&cimagelock);
222                 }
223                 free(src);
224                 incref(ci);
225                 i->aux = ci;
226                 p->cimage = erealloc(p->cimage, ++p->ncimage*sizeof(Cimage *));
227                 p->cimage[p->ncimage-1] = ci;
228                 p->changed = TRUE;
229                 addrefresh(p, "");
230         }
231 }
232
233 static char *mimetab[] = {
234         "text/html",
235         "application/xhtml",
236         nil,
237 };
238
239 static
240 void
241 pageloadproc(void *v)
242 {
243         Page *p;
244         char buf[BUFSIZE], cs[32], *s;
245         long n, l;
246         int fd, i, ctype;
247
248         threadsetname("pageloadproc");
249         rfork(RFFDG);
250
251         p = v;
252         addrefresh(p, "opening: %S...", p->url->src.r);
253         fd = urlopen(p->url);
254         if(fd < 0){
255                 addrefresh(p, "%S: %r", p->url->src.r);
256     Err:
257                 p->loading = FALSE;
258                 return;
259         }
260         if(runestrlen(p->url->ctype.r) == 0) /* assume .html when headers don't say anyting */
261                 goto Html;
262
263         snprint(buf, sizeof(buf), "%S", p->url->ctype.r);
264         for(i=0; mimetab[i]!=nil; i++)
265                 if(cistrncmp(buf, mimetab[i], strlen(mimetab[i])) == 0)
266                         break;
267
268         if(mimetab[i]){
269     Html:
270                 ctype = TextHtml;
271         }else if(cistrncmp(buf, "text/", 5) == 0)
272                 ctype = TextPlain;
273         else{
274                 close(fd);
275                 addrefresh(p, "%S: unsupported mime type: '%S'", p->url->act.r, p->url->ctype.r);
276                 goto Err;
277         }
278         addrefresh(p, "loading: %S...", p->url->src.r);
279
280         s = nil;
281         if(p->url->ctype.nr > 0){
282                 snprint(buf, sizeof(buf), "%.*S", p->url->ctype.nr, p->url->ctype.r);
283                 if(findctype(cs, sizeof(cs), "charset", buf) == 0)
284                         s = cs;
285         }
286         if((fd = pipeline(fd, s != nil ? "uhtml -c %q" : "uhtml", s)) < 0)
287                 goto Err;
288
289         s = nil;
290         l = 0;
291         while((n=read(fd, buf, sizeof(buf))) > 0){
292                 if(p->aborting){
293                         if(s){
294                                 free(s);
295                                 s = nil;
296                         }
297                         break;
298                 }
299                 s = erealloc(s, l+n+1);
300                 memmove(s+l, buf, n);
301                 l += n;
302                 s[l] = '\0';
303         }
304         close(fd);
305         n = l;
306         if(s){
307                 p->items = parsehtml((uchar *)s, n, p->url->act.r, ctype, UTF_8, &p->doc);
308                 free(s);
309                 fixtext(p);
310                 if(ctype==TextHtml && p->aborting==FALSE){
311                         p->changed = TRUE;
312                         addrefresh(p, "");
313                         if(p->doc->doctitle){
314                                 p->title.r = erunestrdup(p->doc->doctitle);
315                                 p->title.nr = runestrlen(p->title.r);
316                         }
317                         p->loading = XXX;
318                         if(p->doc->kidinfo)
319                                 loadchilds(p, p->doc->kidinfo);
320                         else if(p->doc->images)
321                                 loadimages(p);
322                 }
323         }
324         p->changed = TRUE;
325         p->loading = FALSE;
326         addrefresh(p, "");
327 }
328
329 static
330 void
331 pageload1(Page *p, Url *u, int dohist)
332 {
333         pageclose(p);
334         p->loading = TRUE;
335         p->url = u;
336         if(dohist)
337                 winaddhist(p->w, p->url);
338         proccreate(pageloadproc, p, STACK);
339 }
340
341 void
342 pageload(Page *p, Url *u, int dohist)
343 {
344         if(p->parent == nil)
345                 textset(&p->w->url, u->src.r, u->src.nr);
346         draw(p->b, p->all, display->white, nil, ZP);
347         pageload1(p, u, dohist);
348 }
349
350 void
351 pageget(Page *p, Runestr *src, Runestr *post,  int m, int dohist)
352 {
353         pageload(p, urlalloc(src, post, m), dohist);
354 }
355
356 void
357 pageclose(Page *p)
358 {
359         Page *c, *nc;
360
361         if(p == selpage)
362                 selpage = nil;
363         pageabort(p);
364         closeimages(p);
365         urlfree(p->url);
366         p->url = nil;
367         if(p->doc){
368                 freedocinfo(p->doc);
369                 p->doc = nil;
370         }
371         layfree(p->lay);
372         p->lay = nil;
373         freeitems(p->items);
374         p->items = nil;
375         for(c=p->child; c!=nil; c=nc){
376                 nc = c->next;
377                 pageclose(c);
378                 free(c);
379         }
380         p->child = nil;
381         closerunestr(&p->title);
382         closerunestr(&p->refresh.rs);
383         p->refresh.t = 0;
384         p->pos = ZP;
385         p->top = ZP;
386         p->bot = ZP;
387         p->loading = p->aborting = FALSE;
388 }
389
390 int
391 pageabort(Page *p)
392 {
393         Page *c;
394
395         for(c=p->child; c!=nil; c=c->next)
396                 pageabort(c);
397
398         p->aborting = TRUE;
399         while(p->loading)
400                 sleep(100);
401
402         p->aborting = FALSE;
403         return TRUE;
404 }
405
406
407 static Image *tmp;
408
409 void
410 tmpresize(void)
411 {
412         if(tmp)
413                 freeimage(tmp);
414
415         tmp = eallocimage(display, Rect(0,0,Dx(screen->r),Dy(screen->r)), screen->chan, 0, -1);
416 }
417
418 static
419 void
420 renderchilds(Page *p)
421 {
422         Rectangle r;
423         Kidinfo *k;
424         Page *c;
425         int i, j, x, y, *w, *h;
426
427         draw(p->b, p->all, display->white, nil, ZP);
428         r = p->all;
429         y = r.min.y;
430         c = p->child;
431         k = p->kidinfo;
432         frdims(k->rows, k->nrows, Dy(r), &h);
433         frdims(k->cols, k->ncols, Dx(r), &w);
434         for(i=0; i<k->nrows; i++){
435                 x = r.min.x;
436                 for(j=0; j<k->ncols; j++){
437                         if(c->aborting)
438                                 return;
439                         c->b = p->b;
440                         c->all = Rect(x,y,x+w[j],y+h[i]);
441                         c->w = p->w;
442                         pagerender(c);
443                         c = c->next;
444                         x += w[j];
445                 }
446                 y += h[i];
447         }
448         free(w);
449         free(h);
450 }
451
452 static
453 void
454 pagerender1(Page *p)
455 {
456         Rectangle r;
457
458         r = p->all;
459         p->hscrollr = r;
460         p->hscrollr.min.y = r.max.y-Scrollsize;
461         p->vscrollr = r;
462         p->vscrollr.max.x = r.min.x+Scrollsize;
463         r.max.y -= Scrollsize;
464         r.min.x += Scrollsize;
465         p->r = r;
466         p->vscrollr.max.y = r.max.y;
467         p->hscrollr.min.x = r.min.x;
468         laypage(p);
469         pageredraw(p);
470 }
471
472 void
473 pagerender(Page *p)
474 {
475         if(p->child && p->loading==FALSE)
476                 renderchilds(p);
477         else if(p->doc)
478                 pagerender1(p);
479 }
480
481 void
482 pageredraw(Page *p)
483 {
484         Rectangle r;
485
486         r = p->lay->r;
487         if(Dx(r)==0 || Dy(r)==0){
488                 draw(p->b, p->r, display->white, nil, ZP);
489                 return;
490         }
491         if(tmp == nil)
492                 tmpresize();
493
494         p->selecting = FALSE;
495         draw(tmp, tmp->r, getbg(p), nil, ZP);
496         laydraw(p, tmp, p->lay);
497         draw(p->b, p->r, tmp, nil, tmp->r.min);
498         r = p->vscrollr;
499         r.min.y = r.max.y;
500         r.max.y += Scrollsize;
501         draw(p->b, r, tagcols[HIGH], nil, ZP);
502         draw(p->b, insetrect(r, 1), tagcols[BACK], nil, ZP);
503         pagescrldraw(p);
504 }
505
506 static
507 void
508 pageselect1(Page *p)    /* when called, button 1 is down */
509 {
510         Point mp, npos, opos;
511         int b, scrled, x, y;
512
513         b = mouse->buttons;
514         mp = mousectl->xy;
515         opos = getpt(p, mp);
516         do{
517                 x = y = 0;
518                 if(mp.x < p->r.min.x)
519                         x -= p->r.min.x-mp.x;
520                 else if(mp.x > p->r.max.x)
521                         x += mp.x-p->r.max.x;
522                 if(mp.y < p->r.min.y)
523                         y -= (p->r.min.y-mp.y)*Panspeed;
524                 else if(mp.y > p->r.max.y)
525                         y += (mp.y-p->r.max.y)*Panspeed;
526
527                 scrled = pagescrollxy(p, x, y);
528                 npos = getpt(p, mp);
529                 if(opos.y <  npos.y){
530                         p->top = opos;
531                         p->bot = npos;
532                 }else{
533                         p->top = npos;
534                         p->bot = opos;
535                 }
536                 pageredraw(p);
537                 if(scrled == TRUE)
538                         scrsleep(100);
539                 else
540                         readmouse(mousectl);
541
542                 mp = mousectl->xy;
543         }while(mousectl->buttons == b);
544 }
545
546 static Rune left1[] =  { L'{', L'[', L'(', L'<', L'«', 0 };
547 static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
548 static Rune left2[] =  { L'\'', L'"', L'`', 0 };
549
550 static
551 Rune *left[] = {
552         left1,
553         left2,
554         nil
555 };
556 static
557 Rune *right[] = {
558         right1,
559         left2,
560         nil
561 };
562
563 void
564 pagedoubleclick(Page *p)
565 {
566         Point xy;
567         Line *l;
568         Box *b;
569
570         xy = getpt(p, mouse->xy);
571         l = linewhich(p->lay, xy);
572         if(l==nil || l->hastext==FALSE)
573                 return;
574
575         if(xy.x<l->boxes->r.min.x && hasbrk(l->state)){ /* beginning of line? */
576                 p->top = l->boxes->r.min;
577                 if(l->next && !hasbrk(l->next->state)){
578                         for(l=l->next; l->next!=nil; l=l->next)
579                                 if(hasbrk(l->next->state))
580                                         break;
581                 }
582                 p->bot = l->lastbox->r.max;;
583         }else if(xy.x>l->lastbox->r.max.x && hasbrk(l->next->state)){   /* end of line? */
584                 p->bot = l->lastbox->r.max;
585                 if(!hasbrk(l->state) && l->prev!=nil){
586                         for(l=l->prev; l->prev!=nil; l=l->prev)
587                                 if(hasbrk(l->state))
588                                         break;
589                 }
590                 p->top = l->boxes->r.min;
591         }else{
592                 b = pttobox(l, xy);
593                 if(b!=nil && b->i->tag==Itexttag){
594                         p->top = b->r.min;
595                         p->bot = b->r.max;
596                 }
597         }
598         p->top.y += 2;
599         p->bot.y -= 2;
600         pageredraw(p);
601 }
602
603 static uint clickmsec;
604
605 void
606 pageselect(Page *p)
607 {
608         int b, x, y;
609
610
611         selpage = p;
612         /*
613          * To have double-clicking and chording, we double-click
614          * immediately if it might make sense.
615          */
616         b = mouse->buttons;
617         if(mouse->msec-clickmsec<500){
618                 pagedoubleclick(p);
619                 x = mouse->xy.x;
620                 y = mouse->xy.y;
621                 /* stay here until something interesting happens */
622                 do
623                         readmouse(mousectl);
624                 while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3);
625                 mouse->xy.x = x;        /* in case we're calling pageselect1 */
626                 mouse->xy.y = y;
627         }
628         if(mousectl->buttons == b)
629                 pageselect1(p);
630
631         if(eqpt(p->top, p->bot)){
632                 if(mouse->msec-clickmsec<500)
633                         pagedoubleclick(p);
634                 else
635                         clickmsec = mouse->msec;
636         }
637         while(mouse->buttons){
638                 mouse->msec = 0;
639                 b = mouse->buttons;
640                 if(b & 2)       /* snarf only */
641                         cut(nil, nil, TRUE, FALSE, nil, 0);
642                 while(mouse->buttons == b)
643                         readmouse(mousectl);
644         }
645 }
646
647 Page *
648 pagewhich(Page *p, Point xy)
649 {
650         Page *c;
651
652         if(p->child == nil)
653                 return p;
654
655         for(c=p->child; c!=nil; c=c->next)
656                 if(ptinrect(xy, c->all))
657                         return pagewhich(c, xy);
658
659         return nil;
660 }
661
662 void
663 pagemouse(Page *p, Point xy, int but)
664 {
665         Box *b;
666
667         p = pagewhich(p, xy);
668         if(p == nil)
669                 return;
670
671         if(pagerefresh(p))
672                 return;
673
674         if(p->lay == nil)
675                 return;
676
677         if(ptinrect(xy, p->vscrollr)){
678                 pagescroll(p, but, FALSE);
679                 return;
680         }
681         if(ptinrect(xy, p->hscrollr)){
682                 pagescroll(p, but, TRUE);
683                 return;
684         }
685         xy = getpt(p, xy);
686         b = boxwhich(p->lay, xy);
687         if(b && b->mouse)
688                 b->mouse(b, p, but);
689         else if(but == 1)
690                 pageselect(p);
691 }
692
693 void
694 pagetype(Page *p, Rune r, Point xy)
695 {
696         Box *b;
697         int x, y;
698
699         p = pagewhich(p, xy);
700         if(p == nil)
701                 return;
702
703         if(pagerefresh(p))
704                 return;
705
706         if(p->lay == nil)
707                 return;
708
709         /* text field? */
710         xy = getpt(p, xy);
711         b = boxwhich(p->lay, xy);
712         if(b && b->key){
713                 b->key(b, p, r);
714                 return;
715         }
716         /* ^H: same as 'Back' */
717         if(r == 0x08){
718                 wingohist(p->w, FALSE);
719                 return;
720         }
721
722         x = 0;
723         y = 0;
724         switch(r){
725         case Kleft:
726                 x -= Dx(p->r)/2;
727                 break;
728         case Kright:
729                 x += Dx(p->r)/2;
730                 break;
731         case Kdown:
732         case Kscrollonedown:
733                 y += Dy(p->r)/2;
734                 break;
735         case Kpgdown:
736                 y += Dy(p->r);
737                 break;
738         case Kup:
739         case Kscrolloneup:
740                 y -= Dy(p->r)/2;
741                 break;
742         case Kpgup:
743                 y -= Dy(p->r);
744                 break;
745         case Khome:
746                 y -= Dy(p->lay->r);     /* force p->pos.y = 0 */
747                 break;
748         case Kend:
749                 y = Dy(p->lay->r) - Dy(p->r);
750                 break;
751         default:
752                 return;
753         }
754         if(pagescrollxy(p, x, y))
755                 pageredraw(p);
756 }
757
758 void
759 pagesnarf(Page *p)
760 {
761         Runestr rs;
762
763         memset(&rs, 0, sizeof(Runestr));
764         laysnarf(p, p->lay, &rs);
765         putsnarf(&rs);
766         closerunestr(&rs);
767 }
768
769 void
770 pagesetrefresh(Page *p)
771 {
772         Runestr rs;
773         Rune *s, *q, *t;
774         char *v;
775         int n;
776
777         if(!p->doc || !p->doc->refresh)
778                 return;
779
780         s = p->doc->refresh;
781         q = runestrchr(s, L'=');
782         if(q == nil)
783                 return;
784         q++;
785         if(!q)
786                 return;
787         n = runestrlen(q);
788         if(*q == L'''){
789                 q++;
790                 n -= 2;
791         }
792         if(n <= 0)
793                 return;
794         t = runesmprint("%.*S", n, q);
795         rs.r = urlcombine(getbase(p), t);
796         rs.nr = runestrlen(rs.r);
797         copyrunestr(&p->refresh.rs, &rs);
798         closerunestr(&rs);
799         free(t);
800
801         /* now the time */
802         q = runestrchr(s, L';');
803         if(q){
804                 v = smprint("%.*S", (int)(q-s),  s);
805                 p->refresh.t = atoi(v);
806                 free(v);
807         }else
808                 p->refresh.t = 1;
809
810         p->refresh.t += time(0);
811 }
812
813 int
814 pagerefresh(Page *p)
815 {
816         int t;
817
818         if(!p->refresh.t)
819                 return 0;
820
821         t = p->refresh.t - time(0);
822         if(t > 0)
823                 return 0;
824
825         pageget(p, &p->refresh.rs, nil, HGet, FALSE);
826         return 1;
827 }