aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/IO/IO.c49
-rw-r--r--src/IO/IO.h3
-rw-r--r--src/IO/Makefile.am2
-rw-r--r--src/IO/Url.h6
-rw-r--r--src/IO/about.c130
-rw-r--r--src/IO/dpi.c497
-rw-r--r--src/IO/http.c419
-rw-r--r--src/IO/iowatch.cc6
-rw-r--r--src/IO/mime.c12
-rw-r--r--src/IO/mime.h21
-rw-r--r--src/Makefile.am51
-rw-r--r--src/auth.c539
-rw-r--r--src/auth.h19
-rw-r--r--src/bookmark.c3
-rw-r--r--src/bw.c45
-rw-r--r--src/bw.h16
-rw-r--r--src/cache.c504
-rw-r--r--src/cache.h6
-rw-r--r--src/capi.c393
-rw-r--r--src/capi.h7
-rw-r--r--src/chain.c30
-rw-r--r--src/chain.h1
-rw-r--r--src/colors.c11
-rw-r--r--src/cookies.c89
-rw-r--r--src/cookies.h3
-rw-r--r--src/css.cc617
-rw-r--r--src/css.hh495
-rw-r--r--src/cssparser.cc1476
-rw-r--r--src/cssparser.hh54
-rw-r--r--src/decode.c29
-rw-r--r--src/dgif.h19
-rw-r--r--src/dialog.cc107
-rw-r--r--src/dialog.hh7
-rw-r--r--src/dicache.c375
-rw-r--r--src/dicache.h26
-rw-r--r--src/dillo.cc319
-rw-r--r--src/dir.c75
-rw-r--r--src/dir.h20
-rw-r--r--src/djpeg.h19
-rw-r--r--src/dns.c68
-rw-r--r--src/dns.h8
-rw-r--r--src/doctree.hh72
-rw-r--r--src/dpiapi.c13
-rw-r--r--src/dpng.h19
-rw-r--r--src/findbar.cc40
-rw-r--r--src/findbar.hh23
-rw-r--r--src/form.cc977
-rw-r--r--src/form.hh16
-rw-r--r--src/gif.c108
-rw-r--r--src/history.c38
-rw-r--r--src/history.h6
-rw-r--r--src/html.cc2068
-rw-r--r--src/html.hh3
-rw-r--r--src/html_common.hh86
-rw-r--r--src/image.cc139
-rw-r--r--src/image.hh28
-rw-r--r--src/imgbuf.cc138
-rw-r--r--src/imgbuf.hh30
-rw-r--r--src/jpeg.c121
-rw-r--r--src/keys.cc363
-rw-r--r--src/keys.hh68
-rw-r--r--src/keysrc101
-rw-r--r--src/klist.c3
-rw-r--r--src/menu.cc315
-rw-r--r--src/menu.hh21
-rw-r--r--src/misc.c117
-rw-r--r--src/misc.h2
-rw-r--r--src/msg.h23
-rw-r--r--src/nav.c177
-rw-r--r--src/nav.h8
-rw-r--r--src/paths.cc101
-rw-r--r--src/paths.hh26
-rw-r--r--src/pixmaps.h199
-rw-r--r--src/plain.cc90
-rw-r--r--src/png.c227
-rw-r--r--src/prefs.c528
-rw-r--r--src/prefs.h44
-rw-r--r--src/prefsparser.cc208
-rw-r--r--src/prefsparser.hh24
-rw-r--r--src/styleengine.cc678
-rw-r--r--src/styleengine.hh84
-rw-r--r--src/table.cc251
-rw-r--r--src/table.hh4
-rw-r--r--src/timeout.cc2
-rw-r--r--src/ui.cc375
-rw-r--r--src/ui.hh37
-rw-r--r--src/uicmd.cc586
-rw-r--r--src/uicmd.hh24
-rw-r--r--src/url.c174
-rw-r--r--src/url.h17
-rw-r--r--src/utf8.cc102
-rw-r--r--src/utf8.hh33
-rw-r--r--src/web.cc45
-rw-r--r--src/web.hh5
-rw-r--r--src/xembed.cc165
-rw-r--r--src/xembed.hh23
96 files changed, 11306 insertions, 4445 deletions
diff --git a/src/IO/IO.c b/src/IO/IO.c
index a0e18226..4b0285f2 100644
--- a/src/IO/IO.c
+++ b/src/IO/IO.c
@@ -13,14 +13,9 @@
* Dillo's event driven IO engine
*/
-#include <stdio.h>
-#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <sys/socket.h>
#include "../msg.h"
#include "../chain.h"
#include "../klist.h"
@@ -64,13 +59,13 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
/* IO API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
- * Return a newly created, and initialized, 'io' struct
+ * Return a new, initialized, 'io' struct
*/
-static IOData_t *IO_new(int op, int fd)
+static IOData_t *IO_new(int op)
{
IOData_t *io = dNew0(IOData_t, 1);
io->Op = op;
- io->FD = fd;
+ io->FD = -1;
io->Flags = 0;
io->Key = 0;
io->Buf = dStr_sized_new(IOBufLen);
@@ -138,7 +133,8 @@ static void IO_close_fd(IOData_t *io, int CloseCode)
/* With HTTP, if we close the writing part, the reading one also gets
* closed! (other clients may set 'IOFlag_ForceClose') */
- if ((io->Flags & IOFlag_ForceClose) || (CloseCode == IO_StopRdWr)) {
+ if (((io->Flags & IOFlag_ForceClose) || (CloseCode == IO_StopRdWr)) &&
+ io->FD != -1) {
do
st = close(io->FD);
while (st < 0 && errno == EINTR);
@@ -152,10 +148,10 @@ static void IO_close_fd(IOData_t *io, int CloseCode)
/* Stop the polling on this FD */
if (CloseCode & IO_StopRd) {
events |= DIO_READ;
- }
+ }
if (CloseCode & IO_StopWr) {
events |= DIO_WRITE;
- }
+ }
a_IOwatch_remove_fd(io->FD, events);
_MSG(" end IO close (%d) <=====\n", io->FD);
}
@@ -314,6 +310,11 @@ static void IO_fd_write_cb(int fd, void *data)
*/
static void IO_submit(IOData_t *r_io)
{
+ if (r_io->FD < 0) {
+ MSG_ERR("IO_submit: FD not initialized\n");
+ return;
+ }
+
/* Insert this IO in ValidIOs */
IO_ins(r_io);
@@ -351,14 +352,18 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
/* Write data using select */
switch (Op) {
case OpStart:
- io = IO_new(IOWrite, *(int*)Data1); /* SockFD */
+ io = IO_new(IOWrite);
Info->LocalKey = io;
break;
case OpSend:
io = Info->LocalKey;
- dbuf = Data1;
- dStr_append_l(io->Buf, dbuf->Buf, dbuf->Size);
- IO_submit(io);
+ if (Data2 && !strcmp(Data2, "FD")) {
+ io->FD = *(int*)Data1; /* SockFD */
+ } else {
+ dbuf = Data1;
+ dStr_append_l(io->Buf, dbuf->Buf, dbuf->Size);
+ IO_submit(io);
+ }
break;
case OpEnd:
case OpAbort:
@@ -376,7 +381,7 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* FWD */
+ } else { /* 1 FWD */
/* Write-data status */
switch (Op) {
default:
@@ -390,10 +395,16 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
/* This part catches the reader's messages */
switch (Op) {
case OpStart:
- io = IO_new(IORead, *(int*)Data2); /* SockFD */
+ io = IO_new(IORead);
Info->LocalKey = io;
io->Info = Info;
- IO_submit(io);
+ break;
+ case OpSend:
+ io = Info->LocalKey;
+ if (Data2 && !strcmp(Data2, "FD")) {
+ io->FD = *(int*)Data1; /* SockFD */
+ IO_submit(io);
+ }
break;
case OpAbort:
io = Info->LocalKey;
@@ -405,7 +416,7 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* FWD */
+ } else { /* 2 FWD */
/* Send read-data */
io = Data1;
switch (Op) {
diff --git a/src/IO/IO.h b/src/IO/IO.h
index 65b032f5..b75488c2 100644
--- a/src/IO/IO.h
+++ b/src/IO/IO.h
@@ -1,9 +1,6 @@
#ifndef __IO_H__
#define __IO_H__
-#include <unistd.h>
-#include <sys/uio.h>
-
#include "d_size.h"
#include "../../dlib/dlib.h"
#include "../chain.h"
diff --git a/src/IO/Makefile.am b/src/IO/Makefile.am
index bc2dea7e..b168073c 100644
--- a/src/IO/Makefile.am
+++ b/src/IO/Makefile.am
@@ -1,3 +1,4 @@
+AM_CPPFLAGS=-DDILLO_BINDIR='"$(bindir)/"'
AM_CFLAGS = @LIBFLTK_CFLAGS@
AM_CXXFLAGS = @LIBFLTK_CXXFLAGS@
@@ -8,7 +9,6 @@ libDiof_a_SOURCES = \
mime.h \
about.c \
Url.h \
- proto.c \
http.c \
dpi.c \
IO.c \
diff --git a/src/IO/Url.h b/src/IO/Url.h
index 698bc6ad..95919f11 100644
--- a/src/IO/Url.h
+++ b/src/IO/Url.h
@@ -16,19 +16,19 @@ extern void a_Http_freeall(void);
int a_Http_init(void);
int a_Http_proxy_auth(void);
void a_Http_set_proxy_passwd(const char *str);
+char *a_Http_make_connect_str(const DilloUrl *url);
+const char *a_Http_get_proxy_urlstr();
Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy);
void a_Http_ccc (int Op, int Branch, int Dir, ChainLink *Info,
void *Data1, void *Data2);
-void a_About_ccc(int Op, int Branch, int Dir, ChainLink *Info,
- void *Data1, void *Data2);
void a_IO_ccc (int Op, int Branch, int Dir, ChainLink *Info,
void *Data1, void *Data2);
void a_Dpi_ccc (int Op, int Branch, int Dir, ChainLink *Info,
void *Data1, void *Data2);
char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd);
-void a_Dpi_bye_dpid(void);
+void a_Dpi_dillo_exit(void);
void a_Dpi_init(void);
diff --git a/src/IO/about.c b/src/IO/about.c
index 4cd5e2c1..508bfd11 100644
--- a/src/IO/about.c
+++ b/src/IO/about.c
@@ -14,7 +14,7 @@
/*
* HTML text for startup screen
*/
-const char *AboutSplash=
+const char *const AboutSplash=
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
"<html>\n"
"<head>\n"
@@ -75,7 +75,7 @@ const char *AboutSplash=
" <tr>\n"
" <td>&nbsp;&nbsp;\n"
" <td>\n"
-" <a href='http://cvs.auriga.wearlab.de/cgi-bin/cvsweb.cgi/dillo2/ChangeLog?rev=HEAD;cvsroot=dillo'>\n"
+" <a href='http://hg.dillo.org/dillo/file/tip/ChangeLog'>\n"
" ChangeLog</a>\n"
" <tr>\n"
" <td>&nbsp;&nbsp;\n"
@@ -129,7 +129,7 @@ const char *AboutSplash=
" <tr>\n"
" <td>&nbsp;&nbsp;\n"
" <td>\n"
-" <a href='http://www.voltairenet.org/'>VoltaireNet</a>\n"
+" <a href='http://www.voltairenet.org/en'>VoltaireNet</a>\n"
" <tr>\n"
" <td>&nbsp;&nbsp;\n"
" <td>\n"
@@ -191,7 +191,7 @@ const char *AboutSplash=
" <tr><td>&nbsp;&nbsp;\n"
" <td><a href='http://www.violence.de'>Peace&amp;Violence</a>\n"
" <tr><td>&nbsp;&nbsp;\n"
-" <td><a href='http://www.gnu.org/philosophy/right-to-read.html'>"
+" <td><a href='http://www.gnu.org/philosophy/right-to-read.html'>\n"
" Right to Read</a>\n"
" </table>\n"
" </table>\n"
@@ -217,7 +217,8 @@ const char *AboutSplash=
" <td bgcolor='#FFFFFF'>\n"
" <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
" <p>\n"
-" Dillo is Free Software in the terms of the GPL3.\n"
+" Dillo is Free Software under the terms of version 3 of the\n"
+" <a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>.\n"
" This means you have four basic freedoms:\n"
" <ul>\n"
" <li>Freedom to use the program any way you see fit.\n"
@@ -225,10 +226,9 @@ const char *AboutSplash=
" <li>Freedom to make backup copies.\n"
" <li>Freedom to redistribute it.\n"
" </ul>\n"
-" The <a href='http://www.gnu.org/licenses/gpl.html'>GPL3</a>\n"
-" is the legal mechanism that gives you these freedoms.\n"
-" It also protects them from being taken away: any derivative work\n"
-" based on the program must be under the GPL3.<br>\n"
+" The GPL is the legal mechanism that gives you these freedoms.\n"
+" It also protects you from having them taken away: any derivative work\n"
+" based on the program must be under GPLv3 as well.<br>\n"
" </table>\n"
"</table>\n"
"</table>\n"
@@ -240,25 +240,19 @@ const char *AboutSplash=
"<tr>\n"
" <td bgcolor='#CCCCCC'>\n"
" <h4>Release overview</h4>\n"
-" October 14, 2008\n"
+" February 11, 2010\n"
"<tr>\n"
" <td bgcolor='#FFFFFF'>\n"
" <table border='0' cellspacing='0' cellpadding='5'>\n"
" <tr>\n"
" <td>\n"
"<p>\n"
-"This is a rewrite of dillo, using FLTK2, that comes\n"
-"with lots of improvements and fixes.\n"
+"This release features a major overhaul of the cookies subsystem,\n"
+"a reimplementation of the DPI API, a configurable connection limit,\n"
+"and various CSS improvements.\n"
"<p>\n"
-"Our users will surely enjoy this new release as it will give them\n"
-"the same things they're accustomed plus tabbed browsing,\n"
-"antialiasing, different\n"
-"character sets, accepting compressed pages, control over image\n"
-"loading, smaller footprint, fewer dependencies, better table\n"
-"rendering, bugfixes, improved GUI, ... In brief, a better dillo.\n"
-"<p>\n"
-"Remember that dillo project uses a release model where every new\n"
-"browser shall be better than the former.\n"
+"Remember that the dillo project uses a release model where every new\n"
+"version shall be better than the last.\n"
"<EM>Keep up with the latest one!</EM>\n"
" </table>\n"
"</table>\n"
@@ -272,7 +266,7 @@ const char *AboutSplash=
" <td bgcolor='#CCCCCC'>\n"
" <h4>ChangeLog highlights</h4>\n"
" (Extracted from the\n"
-" <a href='http://cvs.auriga.wearlab.de/cgi-bin/cvsweb.cgi/dillo2/ChangeLog?rev=HEAD;cvsroot=dillo'>full\n"
+" <a href='http://hg.dillo.org/dillo/file/tip/ChangeLog'>full\n"
" ChangeLog</a>)\n"
"<tr>\n"
" <td bgcolor='#FFFFFF'>\n"
@@ -280,61 +274,27 @@ const char *AboutSplash=
" <tr>\n"
" <td>\n"
"<ul>\n"
-"<li>Ported Dillo from GTK1 to FLTK2."
-"<li>Ported a susbstantial part of the code from C to C++ (FLTK2 is in C++)."
-"<li>Wrote a new library: Dlib. With 'Dlib' Dillo doesn't need glib anymore."
-"<li>Ported all the code to Dlib."
-"<li>Made Dillo's UI Control Panel resizable on-the-fly."
-"<li>Implemented a new, simpler, dillorc parser."
-"<li>Reimplemented the Concomitant Callback chains into a uniform scheme!"
-"<li>Removed threads from IO. Now it only uses select-based watches."
-"<li>Simplified http.c by reusing the new non-blocking writes in IO."
-"<li>Implemented Stop button to not only stop rendering but also networking."
-"<li>Bound Ctrl+Space to toggle fullscreen mode."
-"<li>Added a http_referer preference. See details in dillorc."
-"<li>CCC: added reentrancy control to the OpEnd and OpAbort operations."
-"<li>CCC: enhanced the debug function and implemented OpAbort for dpi."
-"<li>Hooked a decoder for text/plain with charset."
-"<li>Forbid dpi GET and POST from non dpi-generated urls."
-"<li>Implemented tabbed browsing."
-"<li>Added a image-loading toggle button to the UI."
-"<li>Added line numbers and enabled wrapping in the 'View Source' window."
-"<li>Added HTTP-1.1's chunked transfer support!"
-"<li>Made the stop button sensitive when loading an image."
-"<li>Added support for 'charset' in the HTTP header field for Content-Type."
-"<li>Added support for 'charset' in the META element."
-"<li>Added the multipart/form-data encoding method to form submission."
-"<li>Made zlib a configure requirement, and cleaned up configure.in."
-"<li>Enabled the file dpi to look inside gzipped files."
-"<li>Added code for optional image loading (nice interface)!"
-"<li>Fixed data guesser to detect ASCII, LATIN1, UTF8, KOI8-R, CP-1251 as"
-" text."
-"<li>Fixed void to int conversions for 64bit-arch."
-"<li>Set the url resolver to escape illegal chars instead of stripping."
-"<li>Big html.cc cleanup. New classes, form API, source split."
-"<li>Added int32_t, EAI_NODATA and iconv tests for FreeBSD."
-"<li>Replaced the findtext dialog with an in-window widget!"
-"</ul>\n"
-"Dw2:<br>\n"
-"<ul>\n"
-"<li>Enabled clipped redraws (avoids some flickering)."
-"<li>Added combination of drawing rectangles into a larger one."
-"<li>Made getWidgetAtPoint() a virtual method of widget and implemented a"
-" custom one for TextBlock, reducing CPU usage on pages full of links."
-"<li>Set FltkViewBase::draw to intersect with view area for expose."
-"<li>Added double buffering for partial redraws!"
-"<li>Reduced memory usage in 30% by reusing styles, reducing the size"
-" of struct Content, and not preallocating in SimpleVector. !"
-"<li>Moved highlighting information from struct Word into Textblock"
-" to save memory."
-"<li>Reduced memory usage 10% with a custom memory handler in Textblock."
-"<li>Implemented selection of multibyte glyphs (UTF-8)."
-"<li>Fixed a slithery BUG in lout::misc::Stringbuffer."
-"<li>Added 'enter' and 'leave' signals into class Resource."
-"<li>Enabled mouse wheel scrolling."
-"<li>Added setDeleteCallback(DW_Callback_t func, void *data) to widget."
-" This allows to hook a callback when the widget is destroyed."
-"<li>Changed the table-apportion algorithms + bug fixes. Big work!"
+"<li>Added keybindings for scrolling.\n"
+"<li>Help button and local help file.\n"
+"<li>Add support for multiple class names in CSS.\n"
+"<li>Fix X11 coordinate overflows.\n"
+"<li>Improve CSS font parsing.\n"
+"<li>Enable font face setting via &lt;font&gt; element.\n"
+"<li>Ignore XML comment markers in CSS.\n"
+"<li>Fix user agent style for nested &lt;ul&gt;.\n"
+"<li>Handle signed chars. Added dIsspace() and dIsalnum() to dlib.\n"
+"<li>Changed the CCCs to build in one step (for both HTTP and DPI).\n"
+"<li>Remove the empty cache entry lingering after connection abort.\n"
+"<li>Fixed URL unescaping in the datauri DPI.\n"
+"<li>Changed and reimplemented the DPI API.\n"
+"<li>Allow linebreaks around Chinese/Japanese characters.\n"
+"<li>Fix scrolling for text search.\n"
+"<li>Tooltips.\n"
+"<li>Enable popup menu below bottom of page content.\n"
+"<li>Handle JPEGs with CMYK color space.\n"
+"<li>General cookies overhaul.\n"
+"<li>Fixed a bug in w3c_mode.\n"
+"<li>Limit number of simultaneous connections.\n"
"</ul>\n"
" </table>\n"
"</table>\n"
@@ -355,20 +315,20 @@ const char *AboutSplash=
"<ul>\n"
" <li> There's a\n"
" <a href='http://www.dillo.org/dillorc'>dillorc</a>\n"
-" (readable config) file within the tarball; It is well commented\n"
+" (readable config) file within the tarball; It is well-commented\n"
" and has plenty of options to customize dillo, so <STRONG>copy\n"
" it</STRONG> to your <STRONG>~/.dillo/</STRONG> directory, and\n"
-" modify to your taste.\n"
-" <li> There's documentation for developers in the <CODE>/doc</CODE>\n"
+" modify it to your taste.\n"
+" <li> Documentation for developers is in the <CODE>/doc</CODE>\n"
" dir within the tarball; you can find directions on everything\n"
" else at the home page.\n"
-" <li> Dillo has context sensitive menus using the\n"
-" right mouse button (available on pages, links, images,\n"
-" the Back and Forward buttons, and bug meter).\n"
+" <li> The right mouse button brings up a context-sensitive menu\n"
+" (available on pages, links, images, forms, the Back and Forward buttons,\n"
+" and the bug meter).\n"
" <li> Dillo behaves very nicely when browsing local files, images, and HTML.\n"
" It's also very good for Internet searching.\n"
-" <li> This release is mainly intended <strong>for developers</strong>\n"
-" and <em>advanced users</em>.\n"
+" <li> This release is mainly intended for <strong>developers</strong>\n"
+" and <strong>advanced users</strong>.\n"
" <li> Frames, Java and Javascript are not supported.\n"
"</ul>\n"
"<br>\n"
diff --git a/src/IO/dpi.c b/src/IO/dpi.c
index 7c4357da..6f46b2ba 100644
--- a/src/IO/dpi.c
+++ b/src/IO/dpi.c
@@ -21,16 +21,16 @@
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h> /* for errno */
+#include <fcntl.h>
+#include <ctype.h> /* isxdigit */
-#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
@@ -75,7 +75,7 @@ typedef struct {
*/
static Klist_t *ValidConns = NULL; /* Active connections list. It holds
* pointers to dpi_conn_t structures. */
-
+static char SharedKey[32];
/*
* Initialize local data
@@ -92,6 +92,7 @@ static void Dpi_close_fd(int fd)
{
int st;
+ dReturn_if (fd < 0);
do
st = close(fd);
while (st < 0 && errno == EINTR);
@@ -224,14 +225,14 @@ static void Dpi_parse_token(dpi_conn_t *conn)
tag = dStrndup(Tok, (size_t)conn->TokSize);
_MSG("Dpi_parse_token: {%s}\n", tag);
- cmd = a_Dpip_get_attr(Tok, conn->TokSize, "cmd");
+ cmd = a_Dpip_get_attr_l(Tok, conn->TokSize, "cmd");
if (strcmp(cmd, "send_status_message") == 0) {
- msg = a_Dpip_get_attr(Tok, conn->TokSize, "msg");
+ msg = a_Dpip_get_attr_l(Tok, conn->TokSize, "msg");
a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
dFree(msg);
} else if (strcmp(cmd, "chat") == 0) {
- msg = a_Dpip_get_attr(Tok, conn->TokSize, "msg");
+ msg = a_Dpip_get_attr_l(Tok, conn->TokSize, "msg");
a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
dFree(msg);
@@ -241,13 +242,13 @@ static void Dpi_parse_token(dpi_conn_t *conn)
} else if (strcmp(cmd, "start_send_page") == 0) {
conn->Send2EOF = 1;
- urlstr = a_Dpip_get_attr(Tok, conn->TokSize, "url");
+ urlstr = a_Dpip_get_attr_l(Tok, conn->TokSize, "url");
a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
dFree(urlstr);
- /* TODO: a_Dpip_get_attr(Tok, conn->TokSize, "send_mode") */
+ /* TODO: a_Dpip_get_attr_l(Tok, conn->TokSize, "send_mode") */
} else if (strcmp(cmd, "reload_request") == 0) {
- urlstr = a_Dpip_get_attr(Tok, conn->TokSize, "url");
+ urlstr = a_Dpip_get_attr_l(Tok, conn->TokSize, "url");
a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
dFree(urlstr);
}
@@ -260,6 +261,66 @@ static void Dpi_parse_token(dpi_conn_t *conn)
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
+ * Write data into a file descriptor taking care of EINTR
+ * and possible data splits.
+ * Return value: 1 on success, -1 on error.
+ */
+static int Dpi_blocking_write(int fd, const char *msg, int msg_len)
+{
+ int st, sent = 0;
+
+ while (sent < msg_len) {
+ st = write(fd, msg + sent, msg_len - sent);
+ if (st < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ MSG_ERR("[Dpi_blocking_write] %s\n", dStrerror(errno));
+ break;
+ }
+ }
+ sent += st;
+ }
+
+ return (sent == msg_len) ? 1 : -1;
+}
+
+/*
+ * Read all the available data from a filedescriptor.
+ * This is intended for short answers, i.e. when we know the server
+ * will write it all before being preempted. For answers that may come
+ * as an stream with delays, non-blocking is better.
+ * Return value: read data, or NULL on error and no data.
+ */
+static char *Dpi_blocking_read(int fd)
+{
+ int st;
+ const int buf_sz = 8*1024;
+ char buf[buf_sz], *msg = NULL;
+ Dstr *dstr = dStr_sized_new(buf_sz);
+
+ do {
+ st = read(fd, buf, buf_sz);
+ if (st < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ MSG_ERR("[Dpi_blocking_read] %s\n", dStrerror(errno));
+ break;
+ }
+ } else if (st > 0) {
+ dStr_append_l(dstr, buf, st);
+ }
+ } while (st == buf_sz);
+
+ msg = (dstr->len > 0) ? dstr->str : NULL;
+ dStr_free(dstr, (dstr->len > 0) ? FALSE : TRUE);
+ return msg;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/*
* Get a new data buffer (within a 'dbuf'), save it into local data,
* split in tokens and parse the contents.
*/
@@ -290,8 +351,8 @@ static void Dpi_process_dbuf(int Op, void *Data1, dpi_conn_t *conn)
static int Dpi_start_dpid(void)
{
pid_t pid;
- int st_pipe[2], n, ret = 1;
- char buf[16];
+ int st_pipe[2], ret = 1;
+ char *answer;
/* create a pipe to track our child's status */
if (pipe(st_pipe))
@@ -302,15 +363,19 @@ static int Dpi_start_dpid(void)
/* This is the child process. Execute the command. */
char *path1 = dStrconcat(dGethomedir(), "/.dillo/dpid", NULL);
Dpi_close_fd(st_pipe[0]);
- if (execl(path1, "dpid", NULL) == -1) {
+ if (execl(path1, "dpid", (char*)NULL) == -1) {
dFree(path1);
- if (execlp("dpid", "dpid", NULL) == -1) {
- MSG("Dpi_start_dpid (child): %s\n", dStrerror(errno));
- do
- n = write(st_pipe[1], "ERROR", 5);
- while (n == -1 && errno == EINTR);
- Dpi_close_fd(st_pipe[1]);
- _exit (EXIT_FAILURE);
+ path1 = dStrconcat(DILLO_BINDIR, "dpid", NULL);
+ if (execl(path1, "dpid", (char*)NULL) == -1) {
+ dFree(path1);
+ if (execlp("dpid", "dpid", (char*)NULL) == -1) {
+ MSG("Dpi_start_dpid (child): %s\n", dStrerror(errno));
+ if (Dpi_blocking_write(st_pipe[1], "ERROR", 5) == -1) {
+ MSG("Dpi_start_dpid (child): can't write to pipe.\n");
+ }
+ Dpi_close_fd(st_pipe[1]);
+ _exit (EXIT_FAILURE);
+ }
}
}
} else if (pid < 0) {
@@ -323,89 +388,90 @@ static int Dpi_start_dpid(void)
} else {
/* This is the parent process, check our child status... */
Dpi_close_fd(st_pipe[1]);
- do
- n = read(st_pipe[0], buf, 16);
- while (n == -1 && errno == EINTR);
- _MSG("Dpi_start_dpid: n = %d\n", n);
- if (n != 5) {
- ret = 0;
+ if ((answer = Dpi_blocking_read(st_pipe[0])) != NULL) {
+ MSG("Dpi_start_dpid: can't start dpid\n");
+ dFree(answer);
} else {
- MSG("Dpi_start_dpid: %s\n", dStrerror(errno));
+ ret = 0;
}
+ Dpi_close_fd(st_pipe[0]);
}
return ret;
}
/*
- * Make a connection test for a UDS.
- * Return: 0 OK, 1 Not working.
+ * Read dpid's communication keys from its saved file.
+ * Return value: 1 on success, -1 on error.
*/
-static int Dpi_check_uds(char *uds_name)
+static int Dpi_read_comm_keys(int *port)
{
- struct sockaddr_un pun;
- int SockFD, ret = 1;
-
- if (access(uds_name, W_OK) == 0) {
- /* socket connection test */
- memset(&pun, 0, sizeof(struct sockaddr_un));
- pun.sun_family = AF_LOCAL;
- strncpy(pun.sun_path, uds_name, sizeof (pun.sun_path));
-
- if ((SockFD = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1 ||
- connect(SockFD, (void*)&pun, D_SUN_LEN(&pun)) == -1) {
- MSG("Dpi_check_uds: %s %s\n", dStrerror(errno), uds_name);
- } else {
- Dpi_close_fd(SockFD);
- ret = 0;
- }
+ FILE *In;
+ char *fname, *rcline = NULL, *tail;
+ int i, ret = -1;
+
+ fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
+ if ((In = fopen(fname, "r")) == NULL) {
+ MSG_ERR("[Dpi_read_comm_keys] %s\n", dStrerror(errno));
+ } else if ((rcline = dGetline(In)) == NULL) {
+ MSG_ERR("[Dpi_read_comm_keys] empty file: %s\n", fname);
+ } else {
+ *port = strtol(rcline, &tail, 10);
+ for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
+ SharedKey[i] = tail[i+1];
+ SharedKey[i] = 0;
+ ret = 1;
}
+ if (In)
+ fclose(In);
+ dFree(rcline);
+ dFree(fname);
+
return ret;
}
/*
- * Return the directory where the UDS are in,
- * NULL if it can't be found.
+ * Return a socket file descriptor
*/
-static char *Dpi_get_dpid_uds_dir(void)
+static int Dpi_make_socket_fd()
{
- FILE *in;
- char *saved_name_filename; /* :) */
- char dpid_uds_dir[256], *p = NULL;
-
- saved_name_filename =
- dStrconcat(dGethomedir(), "/.dillo/dpi_socket_dir", NULL);
- in = fopen(saved_name_filename, "r");
- dFree(saved_name_filename);
-
- if (in != NULL) {
- fgets(dpid_uds_dir, 256, in);
- fclose(in);
- if ((p = strchr(dpid_uds_dir, '\n'))) {
- *p = 0;
- }
- if (access(dpid_uds_dir, F_OK) == 0) {
- p = dStrdup(dpid_uds_dir);
- _MSG("Dpi_get_dpid_uds_dir:: %s\n", p);
- }
- }
+ int fd, one = 1, ret = -1;
- _MSG("Dpi_get_dpid_uds_dir: %s \n", dStrerror(errno));
- return p;
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
+ /* avoid delays when sending small pieces of data */
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+ ret = fd;
+ }
+ return ret;
}
/*
- * Return the dpid's UDS name, NULL on failure.
+ * Make a connection test for a IDS.
+ * Return: 1 OK, -1 Not working.
*/
-static char *Dpi_get_dpid_uds_name(void)
+static int Dpi_check_dpid_ids()
{
- char *dpid_uds_dir, *dpid_uds_name = NULL;
-
- if ((dpid_uds_dir = Dpi_get_dpid_uds_dir()) != NULL)
- dpid_uds_name= dStrconcat(dpid_uds_dir, "/", "dpid.srs", NULL);
-
- dFree(dpid_uds_dir);
- return dpid_uds_name;
+ struct sockaddr_in sin;
+ const socklen_t sin_sz = sizeof(sin);
+ int sock_fd, dpid_port, ret = -1;
+
+ /* socket connection test */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (Dpi_read_comm_keys(&dpid_port) != -1) {
+ sin.sin_port = htons(dpid_port);
+ if ((sock_fd = Dpi_make_socket_fd()) == -1) {
+ MSG("Dpi_check_dpid_ids: sock_fd=%d %s\n", sock_fd, dStrerror(errno));
+ } else if (connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
+ MSG("Dpi_check_dpid_ids: %s\n", dStrerror(errno));
+ } else {
+ Dpi_close_fd(sock_fd);
+ ret = 1;
+ }
+ }
+ return ret;
}
/*
@@ -415,20 +481,16 @@ static char *Dpi_get_dpid_uds_name(void)
static int Dpi_check_dpid(int num_tries)
{
static int starting = 0;
- char *dpid_uds_name;
int check_st = 1, ret = 2;
- if ((dpid_uds_name = Dpi_get_dpid_uds_name()))
- check_st = Dpi_check_uds(dpid_uds_name);
-
- _MSG("Dpi_check_dpid: dpid_uds_name=%s, check_st=%d\n",
- dpid_uds_name, check_st);
+ check_st = Dpi_check_dpid_ids();
+ _MSG("Dpi_check_dpid: check_st=%d\n", check_st);
- if (check_st == 0) {
+ if (check_st == 1) {
/* connection test with dpi server passed */
starting = 0;
ret = 0;
- } else if (!dpid_uds_name || check_st) {
+ } else {
if (!starting) {
/* start dpid */
if (Dpi_start_dpid() == 0) {
@@ -436,6 +498,7 @@ static int Dpi_check_dpid(int num_tries)
ret = 1;
}
} else if (++starting < num_tries) {
+ /* starting */
ret = 1;
} else {
/* we waited too much, report an error... */
@@ -443,7 +506,6 @@ static int Dpi_check_dpid(int num_tries)
}
}
- dFree(dpid_uds_name);
_MSG("Dpi_check_dpid:: %s\n",
(ret == 0) ? "OK" : (ret == 1 ? "EAGAIN" : "ERROR"));
return ret;
@@ -467,132 +529,137 @@ static int Dpi_blocking_start_dpid(void)
}
/*
- * Return the UDS name of a dpi server.
+ * Return the dpi server's port number, or -1 on error.
* (A query is sent to dpid and then its answer parsed)
* note: as the available servers and/or the dpi socket directory can
* change at any time, we'll ask each time. If someday we find
* that connecting each time significantly degrades performance,
* an optimized approach can be tried.
*/
-static char *Dpi_get_server_uds_name(const char *server_name)
+static int Dpi_get_server_port(const char *server_name)
{
- char *dpid_uds_dir, *dpid_uds_name = NULL,
- *server_uds_name = NULL;
- int st;
-
- dReturn_val_if_fail (server_name != NULL, NULL);
- _MSG("Dpi_get_server_uds_name:: server_name = [%s]\n", server_name);
-
- dpid_uds_dir = Dpi_get_dpid_uds_dir();
- if (dpid_uds_dir) {
- struct sockaddr_un dpid;
- int sock, req_sz, rdlen;
- char buf[128], *cmd, *request, *rply;
- size_t buflen;
-
- /* Get the server's uds name from dpid */
- sock = socket(AF_LOCAL, SOCK_STREAM, 0);
- dpid.sun_family = AF_LOCAL;
- dpid_uds_name = dStrconcat(dpid_uds_dir, "/", "dpid.srs", NULL);
- _MSG("dpid_uds_name = [%s]\n", dpid_uds_name);
- strncpy(dpid.sun_path, dpid_uds_name, sizeof(dpid.sun_path));
-
- if (connect(sock, (struct sockaddr *) &dpid, sizeof(dpid)) == -1)
- perror("connect");
- /* ask dpid to check the server plugin and send its UDS name back */
+ int sock_fd = -1, dpi_port = -1;
+ int dpid_port, ok = 0;
+ struct sockaddr_in sin;
+ char *cmd, *request, *rply = NULL, *port_str;
+ socklen_t sin_sz;
+
+ dReturn_val_if_fail (server_name != NULL, dpi_port);
+ _MSG("Dpi_get_server_port:: server_name = [%s]\n", server_name);
+
+ /* Read dpid's port from saved file */
+ if (Dpi_read_comm_keys(&dpid_port) != -1) {
+ ok = 1;
+ }
+ if (ok) {
+ /* Connect a socket with dpid */
+ ok = 0;
+ sin_sz = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(dpid_port);
+ if ((sock_fd = Dpi_make_socket_fd()) == -1 ||
+ connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
+ MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
+ } else {
+ ok = 1;
+ }
+ }
+ if (ok) {
+ /* ask dpid to check the dpi and send its port number back */
+ ok = 0;
request = a_Dpip_build_cmd("cmd=%s msg=%s", "check_server", server_name);
_MSG("[%s]\n", request);
- do
- st = write(sock, request, strlen(request));
- while (st < 0 && errno == EINTR);
- if (st < 0 && errno != EINTR)
- perror("writing request");
- dFree(request);
- shutdown(sock, 1); /* signals no more writes to dpid */
+ if (Dpi_blocking_write(sock_fd, request, strlen(request)) == -1) {
+ MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
+ } else {
+ ok = 1;
+ }
+ dFree(request);
+ }
+ if (ok) {
/* Get the reply */
- rply = NULL;
- buf[0] = '\0';
- buflen = sizeof(buf)/sizeof(buf[0]);
- for (req_sz = 0; (rdlen = read(sock, buf, buflen)) != 0;
- req_sz += rdlen) {
- if (rdlen == -1 && errno == EINTR)
- continue;
- if (rdlen == -1) {
- perror(" ** Dpi_get_server_uds_name **");
- break;
- }
- rply = dRealloc(rply, (uint_t)(req_sz + rdlen + 1));
- if (req_sz == 0)
- rply[0] = '\0';
- strncat(rply, buf, (size_t)rdlen);
+ ok = 0;
+ if ((rply = Dpi_blocking_read(sock_fd)) == NULL) {
+ MSG("Dpi_get_server_port: can't read server port from dpid.\n");
+ } else {
+ ok = 1;
}
- Dpi_close_fd(sock);
- _MSG("rply = [%s]\n", rply);
-
+ }
+ if (ok) {
/* Parse reply */
- if (rdlen == 0 && rply) {
- cmd = a_Dpip_get_attr(rply, (int)strlen(rply), "cmd");
- if (strcmp(cmd, "send_data") == 0)
- server_uds_name = a_Dpip_get_attr(rply, (int)strlen(rply), "msg");
- dFree(cmd);
- dFree(rply);
+ ok = 0;
+ cmd = a_Dpip_get_attr(rply, "cmd");
+ if (strcmp(cmd, "send_data") == 0) {
+ port_str = a_Dpip_get_attr(rply, "msg");
+ _MSG("Dpi_get_server_port: rply=%s\n", rply);
+ _MSG("Dpi_get_server_port: port_str=%s\n", port_str);
+ dpi_port = strtol(port_str, NULL, 10);
+ dFree(port_str);
+ ok = 1;
}
+ dFree(cmd);
}
- dFree(dpid_uds_dir);
- dFree(dpid_uds_name);
- _MSG("Dpi_get_server_uds_name:: %s\n", server_uds_name);
- return server_uds_name;
+ dFree(rply);
+ Dpi_close_fd(sock_fd);
+
+ return ok ? dpi_port : -1;
}
/*
* Connect a socket to a dpi server and return the socket's FD.
- * We have to ask 'dpid' (dpi daemon) for the UDS of the target dpi server.
+ * We have to ask 'dpid' (dpi daemon) for the port of the target dpi server.
* Once we have it, then the proper file descriptor is returned (-1 on error).
*/
static int Dpi_connect_socket(const char *server_name, int retry)
{
- char *server_uds_name;
- struct sockaddr_un pun;
- int SockFD, err;
-
- /* Query dpid for the UDS name for this server */
- server_uds_name = Dpi_get_server_uds_name(server_name);
- _MSG("server_uds_name = [%s]\n", server_uds_name);
+ struct sockaddr_in sin;
+ int sock_fd, err, dpi_port, ret=-1;
+ char *cmd = NULL;
- if (access(server_uds_name, F_OK) != 0) {
- MSG("server socket was NOT found\n");
+ /* Query dpid for the port number for this server */
+ if ((dpi_port = Dpi_get_server_port(server_name)) == -1) {
+ _MSG("Dpi_connect_socket:: can't get port number for %s\n", server_name);
return -1;
}
+ _MSG("Dpi_connect_socket: server=%s port=%d\n", server_name, dpi_port);
/* connect with this server's socket */
- memset(&pun, 0, sizeof(struct sockaddr_un));
- pun.sun_family = AF_LOCAL;
- strncpy(pun.sun_path, server_uds_name, sizeof (pun.sun_path));
- dFree(server_uds_name);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(dpi_port);
- if ((SockFD = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1)
+ if ((sock_fd = Dpi_make_socket_fd()) == -1) {
perror("[dpi::socket]");
- else if (connect(SockFD, (void*)&pun, D_SUN_LEN(&pun)) == -1) {
+ } else if (connect(sock_fd, (void*)&sin, sizeof(sin)) == -1) {
err = errno;
- SockFD = -1;
+ sock_fd = -1;
MSG("[dpi::connect] errno:%d %s\n", errno, dStrerror(errno));
if (retry) {
switch (err) {
case ECONNREFUSED: case EBADF: case ENOTSOCK: case EADDRNOTAVAIL:
- /* the server may crash and its socket name survive */
- unlink(pun.sun_path);
- SockFD = Dpi_connect_socket(server_name, FALSE);
+ sock_fd = Dpi_connect_socket(server_name, FALSE);
break;
}
}
+
+ /* send authentication Key (the server closes sock_fd on error) */
+ } else if (!(cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey))) {
+ MSG_ERR("[Dpi_connect_socket] Can't make auth message.\n");
+ } else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
+ MSG_ERR("[Dpi_connect_socket] Can't send auth message.\n");
+ } else {
+ ret = sock_fd;
}
+ dFree(cmd);
- return SockFD;
+ return ret;
}
-
/*
* CCC function for the Dpi module
*/
@@ -616,13 +683,13 @@ void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
*fd = SockFD;
Info->LocalKey = fd;
a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 1, 1);
- a_Chain_bcb(OpStart, Info, Info->LocalKey, NULL);
- /* tell the capi to start the receiving branch */
- a_Chain_fcb(OpSend, Info, Info->LocalKey, "SockFD");
+ a_Chain_bcb(OpStart, Info, NULL, NULL);
}
}
if (st == 0 && SockFD != -1) {
+ a_Chain_bcb(OpSend, Info, &SockFD, "FD");
+ a_Chain_fcb(OpSend, Info, &SockFD, "FD");
a_Chain_fcb(OpSend, Info, NULL, "DpidOK");
} else {
MSG_ERR("dpi.c: can't start dpi daemon\n");
@@ -646,7 +713,7 @@ void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* FWD */
+ } else { /* 1 FWD */
/* Send commands to dpi-server (status) */
switch (Op) {
case OpAbort:
@@ -676,7 +743,7 @@ void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* BCK */
+ } else { /* 2 BCK */
switch (Op) {
case OpStart:
conn = Dpi_conn_new(Info);
@@ -688,7 +755,12 @@ void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
}
a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 2, 2);
- a_Chain_bcb(OpStart, Info, NULL, Data1); /* IORead, SockFD */
+ a_Chain_bcb(OpStart, Info, NULL, NULL); /* IORead */
+ break;
+ case OpSend:
+ if (Data2 && !strcmp(Data2, "FD")) {
+ a_Chain_bcb(OpSend, Info, Data1, Data2);
+ }
break;
case OpAbort:
a_Chain_bcb(OpAbort, Info, NULL, NULL);
@@ -703,77 +775,38 @@ void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
}
}
-/*! Send DpiBye to dpid
- * Note: currently disabled. Maybe it'd be better to have a
- * dpid_idle_timeout variable in the config file.
+/*! Let dpid know dillo is no longer running.
+ * Note: currently disabled. It may serve to let the cookies dpi know
+ * when to expire session cookies.
*/
-void a_Dpi_bye_dpid()
+void a_Dpi_dillo_exit()
{
- char *DpiBye_cmd;
- struct sockaddr_un sa;
- size_t sun_path_len, addr_len;
- char *srs_name;
- int new_socket;
-
- srs_name = Dpi_get_dpid_uds_name();
- sun_path_len = sizeof(sa.sun_path);
- addr_len = sizeof(sa);
- sa.sun_family = AF_LOCAL;
-
- if ((new_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
- MSG("a_Dpi_bye_dpid: %s\n", dStrerror(errno));
- }
- strncpy(sa.sun_path, srs_name, sizeof (sa.sun_path));
- if (connect(new_socket, (struct sockaddr *) &sa, addr_len) == -1) {
- MSG("a_Dpi_bye_dpid: %s\n", dStrerror(errno));
- MSG("%s\n", sa.sun_path);
- }
- DpiBye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
- (void) write(new_socket, DpiBye_cmd, strlen(DpiBye_cmd));
- dFree(DpiBye_cmd);
- Dpi_close_fd(new_socket);
}
-
/*
* Send a command to a dpi server, and block until the answer is got.
* Return value: the dpip tag answer as an string, NULL on error.
*/
char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd)
{
- int cst, SockFD;
- ssize_t st;
- char buf[16384], *retval = NULL;
+ int cst, sock_fd;
+ char *ret = NULL;
/* test the dpid, and wait a bit for it to start if necessary */
if ((cst = Dpi_blocking_start_dpid()) != 0) {
- return retval;
+ return ret;
}
- SockFD = Dpi_connect_socket(server_name, TRUE);
- if (SockFD != -1) {
- /* TODO: handle the case of (st < strlen(cmd)) */
- do
- st = write(SockFD, cmd, strlen(cmd));
- while (st == -1 && errno == EINTR);
-
- /* TODO: if the answer is too long... */
- do
- st = read(SockFD, buf, 16384);
- while (st < 0 && errno == EINTR);
-
- if (st == -1)
- perror("[a_Dpi_send_blocking_cmd]");
- else if (st > 0)
- retval = dStrndup(buf, (size_t)st);
-
- Dpi_close_fd(SockFD);
-
- } else {
- perror("[a_Dpi_send_blocking_cmd]");
+ if ((sock_fd = Dpi_connect_socket(server_name, TRUE)) == -1) {
+ MSG_ERR("[a_Dpi_send_blocking_cmd] Can't connect to server.\n");
+ } else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
+ MSG_ERR("[a_Dpi_send_blocking_cmd] Can't send message.\n");
+ } if ((ret = Dpi_blocking_read(sock_fd)) == NULL) {
+ MSG_ERR("[a_Dpi_send_blocking_cmd] Can't read message.\n");
}
+ Dpi_close_fd(sock_fd);
- return retval;
+ return ret;
}
diff --git a/src/IO/http.c b/src/IO/http.c
index 03bb4522..77a1be43 100644
--- a/src/IO/http.c
+++ b/src/IO/http.c
@@ -16,12 +16,12 @@
#include <config.h>
+#include <ctype.h> /* isdigit */
#include <unistd.h>
#include <errno.h> /* for errno */
#include <stdlib.h>
-#include <signal.h>
#include <fcntl.h>
-#include <sys/wait.h>
+#include <assert.h>
#include <sys/socket.h> /* for lots of socket stuff */
#include <netinet/in.h> /* for ntohl and stuff */
#include <arpa/inet.h> /* for inet_ntop */
@@ -33,6 +33,7 @@
#include "../dns.h"
#include "../web.hh"
#include "../cookies.h"
+#include "../auth.h"
#include "../prefs.h"
#include "../misc.h"
@@ -47,34 +48,70 @@ D_STMT_START { \
#define _MSG_BW(web, root, ...)
+static const int HTTP_SOCKET_USE_PROXY = 0x1;
+static const int HTTP_SOCKET_QUEUED = 0x4;
+static const int HTTP_SOCKET_TO_BE_FREED = 0x8;
+
/* 'Url' and 'web' are just references (no need to deallocate them here). */
typedef struct {
int SockFD;
uint_t port; /* need a separate port in order to support PROXY */
- bool_t use_proxy; /* indicates whether to use proxy or not */
+ uint_t flags;
DilloWeb *web; /* reference to client's web structure */
Dlist *addr_list; /* Holds the DNS answer */
int Err; /* Holds the errno of the connect() call */
ChainLink *Info; /* Used for CCC asynchronous operations */
+ char *connected_to; /* Used for per-host connection limit */
} SocketData_t;
+/* Data structures and functions to queue sockets that need to be
+ * delayed due to the per host connection limit.
+ */
+typedef struct SocketQueueEntry {
+ SocketData_t* sock;
+ struct SocketQueueEntry *next ;
+} SocketQueueEntry_t;
+
+typedef struct {
+ SocketQueueEntry_t *head;
+ SocketQueueEntry_t *tail;
+} SocketQueue_t;
+
+typedef struct {
+ char *host;
+ int active_connections;
+ SocketQueue_t queue;
+} HostConnection_t;
+
+static void Http_socket_queue_init(SocketQueue_t *sq);
+static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock);
+static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq);
+static HostConnection_t *Http_host_connection_get(const char *host);
+static void Http_host_connection_remove(HostConnection_t *hc);
+static int Http_connect_socket(ChainLink *Info);
+static void Http_socket_free(int SKey);
/*
* Local data
*/
static Klist_t *ValidSocks = NULL; /* Active sockets list. It holds pointers to
* SocketData_t structures. */
-
static DilloUrl *HTTP_Proxy = NULL;
static char *HTTP_Proxy_Auth_base64 = NULL;
+static char *HTTP_Language_hdr = NULL;
+static Dlist *host_connections;
/*
- * Initialize proxy vars.
+ * Initialize proxy vars and Accept-Language header
*/
int a_Http_init(void)
{
char *env_proxy = getenv("http_proxy");
+ HTTP_Language_hdr = prefs.http_language ?
+ dStrconcat("Accept-Language: ", prefs.http_language, "\r\n", NULL) :
+ dStrdup("");
+
if (env_proxy && strlen(env_proxy))
HTTP_Proxy = a_Url_new(env_proxy, NULL);
if (!HTTP_Proxy && prefs.http_proxy)
@@ -86,6 +123,9 @@ int a_Http_init(void)
if (HTTP_Proxy && prefs.http_proxyuser && strchr(prefs.http_proxyuser, ':'))
HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(prefs.http_proxyuser);
*/
+
+ host_connections = dList_new(5);
+
return 0;
}
@@ -118,6 +158,31 @@ static int Http_sock_new(void)
return a_Klist_insert(&ValidSocks, S);
}
+static void Http_connect_queued_sockets(HostConnection_t *hc)
+{
+ SocketData_t *sd;
+ while (hc->active_connections < prefs.http_max_conns &&
+ (sd = Http_socket_dequeue(&hc->queue))) {
+
+ sd->flags &= ~HTTP_SOCKET_QUEUED;
+
+ if (sd->flags & HTTP_SOCKET_TO_BE_FREED) {
+ dFree(sd);
+ } else if (a_Web_valid(sd->web)) {
+ /* start connecting the socket */
+ if (Http_connect_socket(sd->Info) < 0) {
+ MSG_BW(sd->web, 1, "ERROR: %s", dStrerror(sd->Err));
+ a_Chain_bfcb(OpAbort, sd->Info, NULL, "Both");
+ dFree(sd->Info);
+ Http_socket_free((int) sd->Info->LocalKey);
+ } else {
+ sd->connected_to = hc->host;
+ hc->active_connections++;
+ }
+ }
+ }
+}
+
/*
* Free SocketData_t struct
*/
@@ -127,7 +192,19 @@ static void Http_socket_free(int SKey)
if ((S = a_Klist_get_data(ValidSocks, SKey))) {
a_Klist_remove(ValidSocks, SKey);
- dFree(S);
+
+ if (S->flags & HTTP_SOCKET_QUEUED) {
+ S->flags |= HTTP_SOCKET_TO_BE_FREED;
+ } else {
+ if (S->connected_to) {
+ HostConnection_t *hc = Http_host_connection_get(S->connected_to);
+ hc->active_connections--;
+ Http_connect_queued_sockets(hc);
+ if (hc->active_connections == 0)
+ Http_host_connection_remove(hc);
+ }
+ dFree(S);
+ }
}
}
@@ -167,7 +244,7 @@ static char *Http_get_referer(const DilloUrl *url)
/*
* Generate Content-Type header value for a POST query.
*/
-Dstr *Http_make_content_type(const DilloUrl *url)
+static Dstr *Http_make_content_type(const DilloUrl *url)
{
Dstr *dstr;
@@ -196,16 +273,12 @@ Dstr *Http_make_content_type(const DilloUrl *url)
*/
Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
{
+ const char *auth;
char *ptr, *cookies, *referer;
- Dstr *s_port = dStr_new(""),
- *query = dStr_new(""),
+ Dstr *query = dStr_new(""),
*full_path = dStr_new(""),
*proxy_auth = dStr_new("");
- /* Sending the default port in the query may cause a 302-answer. --Jcid */
- if (URL_PORT(url) && URL_PORT(url) != DILLO_URL_HTTP_PORT)
- dStr_sprintfa(s_port, ":%d", URL_PORT(url));
-
if (use_proxy) {
dStr_sprintfa(full_path, "%s%s",
URL_STR(url),
@@ -224,6 +297,7 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
}
cookies = a_Cookies_get_query(url);
+ auth = a_Auth_get_auth_str(url);
referer = Http_get_referer(url);
if (URL_FLAGS(url) & URL_Post) {
Dstr *content_type = Http_make_content_type(url);
@@ -231,19 +305,22 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
query,
"POST %s HTTP/1.1\r\n"
"Connection: close\r\n"
+ "Accept: text/*,image/*,*/*;q=0.2\r\n"
"Accept-Charset: utf-8,*;q=0.8\r\n"
"Accept-Encoding: gzip\r\n"
- "Host: %s%s\r\n"
+ "%s" /* language */
+ "%s" /* auth */
+ "Host: %s\r\n"
"%s"
"%s"
- "User-Agent: Dillo/%s\r\n"
+ "User-Agent: %s\r\n"
"Content-Length: %ld\r\n"
"Content-Type: %s\r\n"
- "%s"
+ "%s" /* cookies */
"\r\n",
- full_path->str, URL_HOST(url), s_port->str,
- proxy_auth->str, referer, VERSION,
- URL_DATA(url)->len, content_type->str,
+ full_path->str, HTTP_Language_hdr, auth ? auth : "",
+ URL_AUTHORITY(url), proxy_auth->str, referer, prefs.http_user_agent,
+ (long)URL_DATA(url)->len, content_type->str,
cookies);
dStr_append_l(query, URL_DATA(url)->str, URL_DATA(url)->len);
dStr_free(content_type, TRUE);
@@ -253,24 +330,26 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
"GET %s HTTP/1.1\r\n"
"%s"
"Connection: close\r\n"
+ "Accept: text/*,image/*,*/*;q=0.2\r\n"
"Accept-Charset: utf-8,*;q=0.8\r\n"
"Accept-Encoding: gzip\r\n"
- "Host: %s%s\r\n"
- "%s"
+ "%s" /* language */
+ "%s" /* auth */
+ "Host: %s\r\n"
"%s"
- "User-Agent: Dillo/%s\r\n"
"%s"
+ "User-Agent: %s\r\n"
+ "%s" /* cookies */
"\r\n",
full_path->str,
(URL_FLAGS(url) & URL_E2EQuery) ?
"Cache-Control: no-cache\r\nPragma: no-cache\r\n" : "",
- URL_HOST(url), s_port->str,
- proxy_auth->str, referer, VERSION, cookies);
+ HTTP_Language_hdr, auth ? auth : "", URL_AUTHORITY(url),
+ proxy_auth->str, referer, prefs.http_user_agent, cookies);
}
dFree(referer);
dFree(cookies);
- dStr_free(s_port, TRUE);
dStr_free(full_path, TRUE);
dStr_free(proxy_auth, TRUE);
_MSG("Query: {%s}\n", dStr_printable(query, 8192));
@@ -286,7 +365,7 @@ static void Http_send_query(ChainLink *Info, SocketData_t *S)
DataBuf *dbuf;
/* Create the query */
- query = a_Http_make_query_str(S->web->url, S->use_proxy);
+ query = a_Http_make_query_str(S->web->url, S->flags & HTTP_SOCKET_USE_PROXY);
dbuf = a_Chain_dbuf_new(query->str, query->len, 0);
/* actually this message is sent too early.
@@ -294,14 +373,9 @@ static void Http_send_query(ChainLink *Info, SocketData_t *S)
_MSG_BW(S->web, 1, "Sending query to %s...", URL_HOST_(S->web->url));
/* send query */
- a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 1, 1);
- a_Chain_bcb(OpStart, Info, &S->SockFD, NULL);
a_Chain_bcb(OpSend, Info, dbuf, NULL);
dFree(dbuf);
dStr_free(query, 1);
-
- /* Tell the cache to start the receiving CCC for the answer */
- a_Chain_fcb(OpSend, Info, &S->SockFD, "SockFD");
}
/*
@@ -311,7 +385,7 @@ static void Http_send_query(ChainLink *Info, SocketData_t *S)
*/
static int Http_connect_socket(ChainLink *Info)
{
- int status;
+ int i, status;
#ifdef ENABLE_IPV6
struct sockaddr_in6 name;
#else
@@ -324,61 +398,64 @@ static int Http_connect_socket(ChainLink *Info)
S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey));
/* TODO: iterate this address list until success, or end-of-list */
- dh = dList_nth_data(S->addr_list, 0);
-
- if ((S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0) {
- S->Err = errno;
- MSG("Http_connect_socket ERROR: %s\n", dStrerror(errno));
- return -1;
- }
- /* set NONBLOCKING and close on exec. */
- fcntl(S->SockFD, F_SETFL, O_NONBLOCK | fcntl(S->SockFD, F_GETFL));
- fcntl(S->SockFD, F_SETFD, FD_CLOEXEC | fcntl(S->SockFD, F_GETFD));
-
- /* Some OSes require this... */
- memset(&name, 0, sizeof(name));
- /* Set remaining parms. */
- switch (dh->af) {
- case AF_INET:
- {
- struct sockaddr_in *sin = (struct sockaddr_in *)&name;
- socket_len = sizeof(struct sockaddr_in);
- sin->sin_family = dh->af;
- sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
- memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
- if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
- MSG("Connecting to %s\n", inet_ntoa(sin->sin_addr));
- break;
- }
+ for (i = 0; (dh = dList_nth_data(S->addr_list, i)); ++i) {
+ if ((S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ S->Err = errno;
+ MSG("Http_connect_socket ERROR: %s\n", dStrerror(errno));
+ continue;
+ }
+ /* set NONBLOCKING and close on exec. */
+ fcntl(S->SockFD, F_SETFL, O_NONBLOCK | fcntl(S->SockFD, F_GETFL));
+ fcntl(S->SockFD, F_SETFD, FD_CLOEXEC | fcntl(S->SockFD, F_GETFD));
+
+ /* Some OSes require this... */
+ memset(&name, 0, sizeof(name));
+ /* Set remaining parms. */
+ switch (dh->af) {
+ case AF_INET:
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&name;
+ socket_len = sizeof(struct sockaddr_in);
+ sin->sin_family = dh->af;
+ sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
+ memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
+ if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
+ MSG("Connecting to %s\n", inet_ntoa(sin->sin_addr));
+ break;
+ }
#ifdef ENABLE_IPV6
- case AF_INET6:
- {
- char buf[128];
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name;
- socket_len = sizeof(struct sockaddr_in6);
- sin6->sin6_family = dh->af;
- sin6->sin6_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
- memcpy(&sin6->sin6_addr, dh->data, dh->alen);
- inet_ntop(dh->af, dh->data, buf, sizeof(buf));
- if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
- MSG("Connecting to %s\n", buf);
- break;
- }
+ case AF_INET6:
+ {
+ char buf[128];
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name;
+ socket_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = dh->af;
+ sin6->sin6_port =
+ S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
+ memcpy(&sin6->sin6_addr, dh->data, dh->alen);
+ inet_ntop(dh->af, dh->data, buf, sizeof(buf));
+ if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
+ MSG("Connecting to %s\n", buf);
+ break;
+ }
#endif
- }/*switch*/
-
- MSG_BW(S->web, 1, "Contacting host...");
- status = connect(S->SockFD, (struct sockaddr *)&name, socket_len);
- if (status == -1 && errno != EINPROGRESS) {
- S->Err = errno;
- Http_socket_close(S);
- MSG("Http_connect_socket ERROR: %s\n", dStrerror(S->Err));
- return -1;
- } else {
- Http_send_query(S->Info, S);
+ }/*switch*/
+
+ MSG_BW(S->web, 1, "Contacting host...");
+ status = connect(S->SockFD, (struct sockaddr *)&name, socket_len);
+ if (status == -1 && errno != EINPROGRESS) {
+ S->Err = errno;
+ Http_socket_close(S);
+ MSG("Http_connect_socket ERROR: %s\n", dStrerror(S->Err));
+ } else {
+ a_Chain_bcb(OpSend, Info, &S->SockFD, "FD");
+ a_Chain_fcb(OpSend, Info, &S->SockFD, "FD");
+ Http_send_query(S->Info, S);
+ return 0; /* Success */
+ }
}
- return 0; /* Success */
+ return -1;
}
/*
@@ -393,9 +470,15 @@ static int Http_must_use_proxy(const DilloUrl *url)
if (HTTP_Proxy) {
ret = 1;
if (prefs.no_proxy) {
+ const char *host = URL_HOST(url);
+ size_t host_len = strlen(host);
+
np = dStrdup(prefs.no_proxy);
for (p = np; (tok = dStrsep(&p, " ")); ) {
- if (dStristr(URL_AUTHORITY(url), tok)) {
+ int start = host_len - strlen(tok);
+
+ if (start >= 0 && dStrcasecmp(host + start, tok) == 0) {
+ /* no_proxy token is suffix of host string */
ret = 0;
break;
}
@@ -403,42 +486,94 @@ static int Http_must_use_proxy(const DilloUrl *url)
dFree(np);
}
}
+ _MSG("Http_must_use_proxy: %s\n %s\n", URL_STR(url), ret ? "YES":"NO");
return ret;
}
/*
+ * Return a new string for the request used to tunnel HTTPS through a proxy.
+ * As of 2009, the best reference appears to be section 5 of RFC 2817.
+ */
+char *a_Http_make_connect_str(const DilloUrl *url)
+{
+ Dstr *dstr;
+ const char *auth1;
+ int auth_len;
+ char *auth2, *proxy_auth, *retstr;
+
+ dReturn_val_if_fail(Http_must_use_proxy(url), NULL);
+
+ dstr = dStr_new("");
+ auth1 = URL_AUTHORITY(url);
+ auth_len = strlen(auth1);
+ if (auth_len > 0 && !isdigit(auth1[auth_len - 1]))
+ /* if no port number, add HTTPS port */
+ auth2 = dStrconcat(auth1, ":443", NULL);
+ else
+ auth2 = dStrdup(auth1);
+ proxy_auth = HTTP_Proxy_Auth_base64 ?
+ dStrconcat ("Proxy-Authorization: Basic ",
+ HTTP_Proxy_Auth_base64, "\r\n", NULL) :
+ dStrdup("");
+ dStr_sprintfa(
+ dstr,
+ "CONNECT %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "%s"
+ "\r\n",
+ auth2,
+ auth2,
+ proxy_auth);
+
+ dFree(auth2);
+ dFree(proxy_auth);
+ retstr = dstr->str;
+ dStr_free(dstr, 0);
+ return retstr;
+}
+
+/*
+ * Return URL string of HTTP proxy, if any
+ */
+const char *a_Http_get_proxy_urlstr()
+{
+ return HTTP_Proxy ? URL_STR(HTTP_Proxy) : NULL;
+}
+
+/*
* Callback function for the DNS resolver.
* Continue connecting the socket, or abort upon error condition.
* S->web is checked to assert the operation wasn't aborted while waiting.
*/
-void a_Http_dns_cb(int Status, Dlist *addr_list, void *data)
+static void Http_dns_cb(int Status, Dlist *addr_list, void *data)
{
int SKey = VOIDP2INT(data);
SocketData_t *S;
+ HostConnection_t *hc;
S = a_Klist_get_data(ValidSocks, SKey);
if (S) {
if (!a_Web_valid(S->web)) {
- a_Chain_fcb(OpAbort, S->Info, NULL, NULL);
+ a_Chain_bfcb(OpAbort, S->Info, NULL, "Both");
dFree(S->Info);
Http_socket_free(SKey);
} else if (Status == 0 && addr_list) {
/* Successful DNS answer; save the IP */
S->addr_list = addr_list;
- /* start connecting the socket */
- if (Http_connect_socket(S->Info) < 0) {
- MSG_BW(S->web, 1, "ERROR: %s", dStrerror(S->Err));
- a_Chain_fcb(OpAbort, S->Info, NULL, NULL);
- dFree(S->Info);
- Http_socket_free(SKey);
- }
-
+ S->flags |= HTTP_SOCKET_QUEUED;
+ if (S->flags & HTTP_SOCKET_USE_PROXY)
+ hc = Http_host_connection_get(URL_HOST(HTTP_Proxy));
+ else
+ hc = Http_host_connection_get(URL_HOST(S->web->url));
+ Http_socket_enqueue(&hc->queue, S);
+ Http_connect_queued_sockets(hc);
} else {
/* DNS wasn't able to resolve the hostname */
MSG_BW(S->web, 0, "ERROR: Dns can't resolve %s",
- (S->use_proxy) ? URL_HOST_(HTTP_Proxy) : URL_HOST_(S->web->url));
- a_Chain_fcb(OpAbort, S->Info, NULL, NULL);
+ (S->flags & HTTP_SOCKET_USE_PROXY) ? URL_HOST_(HTTP_Proxy) :
+ URL_HOST_(S->web->url));
+ a_Chain_bfcb(OpAbort, S->Info, NULL, "Both");
dFree(S->Info);
Http_socket_free(SKey);
}
@@ -467,11 +602,11 @@ static int Http_get(ChainLink *Info, void *Data1)
if (Http_must_use_proxy(S->web->url)) {
hostname = dStrdup(URL_HOST(HTTP_Proxy));
S->port = URL_PORT(HTTP_Proxy);
- S->use_proxy = TRUE;
+ S->flags |= HTTP_SOCKET_USE_PROXY;
} else {
hostname = dStrdup(URL_HOST(S->web->url));
S->port = URL_PORT(S->web->url);
- S->use_proxy = FALSE;
+ S->flags &= ~HTTP_SOCKET_USE_PROXY;
}
/* Let the user know what we'll do */
@@ -479,7 +614,7 @@ static int Http_get(ChainLink *Info, void *Data1)
/* Let the DNS engine resolve the hostname, and when done,
* we'll try to connect the socket from the callback function */
- a_Dns_resolve(hostname, a_Http_dns_cb, Info->LocalKey);
+ a_Dns_resolve(hostname, Http_dns_cb, Info->LocalKey);
dFree(hostname);
return 0;
@@ -493,6 +628,8 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
{
int SKey = VOIDP2INT(Info->LocalKey);
+ (void)Data2; /* suppress unused parameter warning */
+
dReturn_if_fail( a_Chain_check("a_Http_ccc", Op, Branch, Dir, Info) );
if (Branch == 1) {
@@ -503,6 +640,10 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
/* ( Data1 = Web ) */
SKey = Http_sock_new();
Info->LocalKey = INT2VOIDP(SKey);
+ /* link IO */
+ a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 1, 1);
+ a_Chain_bcb(OpStart, Info, NULL, NULL);
+ /* async. connection */
Http_get(Info, Data1);
break;
case OpEnd:
@@ -518,7 +659,7 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
dFree(Info);
break;
}
- } else { /* FWD */
+ } else { /* 1 FWD */
/* HTTP send-query status branch */
switch (Op) {
default:
@@ -530,6 +671,82 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
}
+static void Http_socket_queue_init(SocketQueue_t *sq)
+{
+ sq->head = NULL;
+ sq->tail = NULL;
+}
+
+static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock)
+{
+ SocketQueueEntry_t *se = dNew(SocketQueueEntry_t, 1);
+
+ se->sock = sock;
+ se->next = NULL;
+
+ if (sq->tail)
+ sq->tail->next = se;
+ sq->tail = se;
+
+ if (! sq->head)
+ sq->head = se;
+}
+
+static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq)
+{
+ SocketQueueEntry_t *se = sq->head;
+ SocketData_t *sd = NULL;
+
+ if (se) {
+ sq->head = se->next;
+ if (sq->tail == se)
+ sq->tail = NULL;
+ sd = se->sock;
+ dFree(se);
+ }
+
+ return sd;
+}
+
+static HostConnection_t *Http_host_connection_get(const char *host)
+{
+ int i;
+ HostConnection_t *hc;
+
+ for (i = 0; i < dList_length(host_connections); i++) {
+ hc = (HostConnection_t*) dList_nth_data(host_connections, i);
+
+ if (dStrcasecmp(host, hc->host) == 0)
+ return hc;
+ }
+
+ hc = dNew0(HostConnection_t, 1);
+ Http_socket_queue_init(&hc->queue);
+ hc->host = dStrdup(host);
+ dList_append(host_connections, hc);
+
+ return hc;
+}
+
+static void Http_host_connection_remove(HostConnection_t *hc)
+{
+ assert(hc->queue.head == NULL);
+ dList_remove_fast(host_connections, hc);
+ dFree(hc->host);
+ dFree(hc);
+}
+
+static void Http_host_connection_remove_all()
+{
+ HostConnection_t *hc;
+
+ while (dList_length(host_connections) > 0) {
+ hc = (HostConnection_t*) dList_nth_data(host_connections, 0);
+ while (Http_socket_dequeue(&hc->queue));
+ Http_host_connection_remove(hc);
+ }
+ dList_free(host_connections);
+}
/*
* Deallocate memory used by http module
@@ -537,7 +754,9 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
*/
void a_Http_freeall(void)
{
+ Http_host_connection_remove_all();
a_Klist_free(&ValidSocks);
a_Url_free(HTTP_Proxy);
dFree(HTTP_Proxy_Auth_base64);
+ dFree(HTTP_Language_hdr);
}
diff --git a/src/IO/iowatch.cc b/src/IO/iowatch.cc
index 0749e24c..f67b711f 100644
--- a/src/IO/iowatch.cc
+++ b/src/IO/iowatch.cc
@@ -22,7 +22,8 @@ using namespace fltk;
//
void a_IOwatch_add_fd(int fd, int when, FileHandler Callback, void *usr_data=0)
{
- add_fd(fd, when, Callback, usr_data);
+ if (fd >= 0)
+ add_fd(fd, when, Callback, usr_data);
}
//
@@ -30,6 +31,7 @@ void a_IOwatch_add_fd(int fd, int when, FileHandler Callback, void *usr_data=0)
//
void a_IOwatch_remove_fd(int fd, int when)
{
- remove_fd(fd, when);
+ if (fd >= 0)
+ remove_fd(fd, when);
}
diff --git a/src/IO/mime.c b/src/IO/mime.c
index 33ec3322..9bffd619 100644
--- a/src/IO/mime.c
+++ b/src/IO/mime.c
@@ -96,16 +96,16 @@ static Viewer_t Mime_major_type_fetch(const char *Key, uint_t Size)
void a_Mime_init()
{
#ifdef ENABLE_GIF
- Mime_add_minor_type("image/gif", a_Gif_image);
+ Mime_add_minor_type("image/gif", a_Dicache_gif_image);
#endif
#ifdef ENABLE_JPEG
- Mime_add_minor_type("image/jpeg", a_Jpeg_image);
- Mime_add_minor_type("image/pjpeg", a_Jpeg_image);
- Mime_add_minor_type("image/jpg", a_Jpeg_image);
+ Mime_add_minor_type("image/jpeg", a_Dicache_jpeg_image);
+ Mime_add_minor_type("image/pjpeg", a_Dicache_jpeg_image);
+ Mime_add_minor_type("image/jpg", a_Dicache_jpeg_image);
#endif
#ifdef ENABLE_PNG
- Mime_add_minor_type("image/png", a_Png_image);
- Mime_add_minor_type("image/x-png", a_Png_image); /* deprecated */
+ Mime_add_minor_type("image/png", a_Dicache_png_image);
+ Mime_add_minor_type("image/x-png", a_Dicache_png_image); /* deprecated */
#endif
Mime_add_minor_type("text/html", a_Html_text);
diff --git a/src/IO/mime.h b/src/IO/mime.h
index 0f51a1e2..0f20cf6d 100644
--- a/src/IO/mime.h
+++ b/src/IO/mime.h
@@ -5,7 +5,7 @@
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*/
@@ -13,7 +13,6 @@
#define __MIME_H__
#include <config.h>
-#include <stddef.h>
#ifdef __cplusplus
extern "C" {
@@ -31,18 +30,12 @@ void *a_Html_text (const char *Type,void *web, CA_Callback_t *Call,
void **Data);
void *a_Plain_text(const char *Type,void *web, CA_Callback_t *Call,
void **Data);
-#ifdef ENABLE_JPEG
-void *a_Jpeg_image(const char *Type,void *web, CA_Callback_t *Call,
- void **Data);
-#endif
-#ifdef ENABLE_PNG
-void *a_Png_image (const char *Type,void *web, CA_Callback_t *Call,
- void **Data);
-#endif
-#ifdef ENABLE_GIF
-void *a_Gif_image (const char *Type,void *web, CA_Callback_t *Call,
- void **Data);
-#endif
+void *a_Dicache_png_image (const char *Type,void *web, CA_Callback_t *Call,
+ void **Data);
+void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data);
+void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data);
/*
* Functions defined inside Mime module
diff --git a/src/Makefile.am b/src/Makefile.am
index d4d03eea..0f09b716 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,25 +1,29 @@
-AM_CPPFLAGS=-DDILLORC_SYS='"$(sysconfdir)/dillorc"' @LIBJPEG_CPPFLAGS@
+AM_CPPFLAGS= \
+ -I$(top_srcdir) \
+ -DDILLO_SYSCONF='"$(sysconfdir)/"' \
+ -DDILLO_DOCDIR='"$(docdir)/"' \
+ @LIBJPEG_CPPFLAGS@
AM_CFLAGS = @LIBPNG_CFLAGS@
-AM_CXXFLAGS = -I.. @LIBPNG_CFLAGS@ @LIBFLTK_CXXFLAGS@
+AM_CXXFLAGS = @LIBPNG_CFLAGS@ @LIBFLTK_CXXFLAGS@
SUBDIRS = IO
bin_PROGRAMS = dillo
dillo_LDADD = \
- ../dlib/libDlib.a \
- ../dpip/libDpip.a \
+ $(top_builddir)/dlib/libDlib.a \
+ $(top_builddir)/dpip/libDpip.a \
IO/libDiof.a \
- ../dw/libDw-widgets.a \
- ../dw/libDw-fltk.a \
- ../dw/libDw-core.a \
- ../lout/liblout.a \
+ $(top_builddir)/dw/libDw-widgets.a \
+ $(top_builddir)/dw/libDw-fltk.a \
+ $(top_builddir)/dw/libDw-core.a \
+ $(top_builddir)/lout/liblout.a \
@LIBJPEG_LIBS@ @LIBPNG_LIBS@ @LIBFLTK_LIBS@ @LIBZ_LIBS@ @LIBICONV_LIBS@
dillo_SOURCES = \
dillo.cc \
- dir.c \
- dir.h \
+ paths.cc \
+ paths.hh \
ui.cc \
ui.hh \
uicmd.cc \
@@ -28,6 +32,8 @@ dillo_SOURCES = \
bw.c \
cookies.c \
cookies.h \
+ auth.c \
+ auth.h \
colors.c \
colors.h \
binaryconst.h \
@@ -37,6 +43,10 @@ dillo_SOURCES = \
history.c \
prefs.c \
prefs.h \
+ prefsparser.cc \
+ prefsparser.hh \
+ keys.cc \
+ keys.hh \
msg.h \
list.h \
url.c \
@@ -47,6 +57,8 @@ dillo_SOURCES = \
klist.h \
chain.c \
chain.h \
+ utf8.cc \
+ utf8.hh \
timeout.cc \
timeout.hh \
dialog.cc \
@@ -65,6 +77,13 @@ dillo_SOURCES = \
dicache.h \
capi.c \
capi.h \
+ css.cc \
+ css.hh \
+ cssparser.cc \
+ cssparser.hh \
+ doctree.hh \
+ styleengine.cc \
+ styleengine.hh \
plain.cc \
html.cc \
html.hh \
@@ -78,8 +97,13 @@ dillo_SOURCES = \
dns.c \
dns.h \
gif.c \
+ dgif.h \
jpeg.c \
+ djpeg.h \
png.c \
+ dpng.h \
+ imgbuf.cc \
+ imgbuf.hh \
image.cc \
image.hh \
menu.hh \
@@ -88,6 +112,9 @@ dillo_SOURCES = \
dpiapi.h \
pixmaps.h \
findbar.cc \
- findbar.hh
+ findbar.hh \
+ xembed.cc \
+ xembed.hh
-EXTRA_DIST = chg srch
+EXTRA_DIST = chg srch keysrc
+sysconf_DATA = keysrc
diff --git a/src/auth.c b/src/auth.c
new file mode 100644
index 00000000..91baca30
--- /dev/null
+++ b/src/auth.c
@@ -0,0 +1,539 @@
+/*
+ * File: auth.c
+ *
+ * Copyright 2008 Jeremy Henty <onepoint@starurchin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+/* Handling of HTTP AUTH takes place here.
+ * This implementation aims to follow RFC 2617:
+ * http://www.ietf.org/rfc/rfc2617.txt
+ */
+
+
+#include <ctype.h> /* iscntrl */
+#include "auth.h"
+#include "msg.h"
+#include "misc.h"
+#include "dialog.hh"
+#include "../dlib/dlib.h"
+
+
+typedef struct {
+ int ok;
+ const char *realm;
+} AuthParse_t;
+
+typedef struct {
+ char *name;
+ Dlist *paths; /* stripped of any trailing '/', so the root path is "" */
+ char *authorization; /* the authorization request header */
+} AuthRealm_t;
+
+typedef struct {
+ char *scheme;
+ char *authority;
+ Dlist *realms;
+} AuthHost_t;
+
+typedef struct {
+ const char *realm_name;
+ const DilloUrl *url;
+} AuthDialogData_t;
+
+/*
+ * Local data
+ */
+static Dlist *auth_hosts;
+
+/*
+ * Initialize the auth module.
+ */
+void a_Auth_init(void)
+{
+ auth_hosts = dList_new(1);
+}
+
+static AuthParse_t *Auth_parse_new()
+{
+ AuthParse_t *auth_parse = dNew(AuthParse_t, 1);
+ auth_parse->ok = 0;
+ auth_parse->realm = NULL;
+ return auth_parse;
+}
+
+static void Auth_parse_free(AuthParse_t *auth_parse)
+{
+ if (auth_parse) {
+ dFree((void *)auth_parse->realm);
+ dFree(auth_parse);
+ }
+}
+
+static int Auth_path_is_inside(const char *path1, const char *path2, int len)
+{
+ /*
+ * path2 is effectively truncated to length len. Typically len will be
+ * strlen(path2), or 1 less when we want to ignore a trailing '/'.
+ */
+ return
+ strncmp(path1, path2, len) == 0 &&
+ (path1[len] == '\0' || path1[len] == '/');
+}
+
+/*
+ * Check valid chars.
+ * Return: 0 if invalid, 1 otherwise.
+ */
+static int Auth_is_token_char(char c)
+{
+ const char *invalid = "\"()<>@,;:\\[]?=/{} \t";
+ return (strchr(invalid, c) || iscntrl((uchar_t)c)) ? 0 : 1;
+}
+
+/*
+ * Unquote the content of a quoted string.
+ * Return: newly allocated unquoted content.
+ *
+ * Arguments:
+ * quoted: pointer to the first char *after* the initial double quote.
+ * size: the number of chars in the result, *not* including a final '\0'.
+ *
+ * Preconditions:
+ * quoted points to a correctly quoted and escaped string.
+ * size is the number of characters in the quoted string, *after*
+ * removing escape characters.
+ *
+ */
+static const char *Auth_unquote_value(const char *quoted, int size)
+{
+ char c, *value, *value_ptr;
+ value_ptr = value = dNew(char, size + 1);
+ while ((c = *quoted++) != '"')
+ *value_ptr++ = (c == '\\') ? *quoted++ : c;
+ *value_ptr = '\0';
+ return value;
+}
+
+/*
+ * Parse a quoted string. Save the result as the auth realm if required.
+ * Return: 1 if the parse succeeds, 0 otherwise.
+ */
+static int Auth_parse_quoted_string(AuthParse_t *auth_parse, int set_realm,
+ char **auth)
+{
+ char *value;
+ int size;
+
+ /* parse the '"' */
+ switch (*(*auth)++) {
+ case '"':
+ break;
+ case '\0':
+ case ',':
+ MSG("auth.c: missing Basic auth token value after '='\n");
+ return 0;
+ break;
+ default:
+ MSG("auth.c: garbage in Basic auth after '='\n");
+ return 0;
+ break;
+ }
+
+ /* parse the rest */
+ value = *auth;
+ size = 0;
+ while (1) {
+ switch (*(*auth)++) {
+ case '"':
+ if (set_realm) {
+ dFree((void *)auth_parse->realm);
+ auth_parse->realm = Auth_unquote_value(value, size);
+ auth_parse->ok = 1;
+ }
+ return 1;
+ break;
+ case '\0':
+ MSG("auth.c: auth string ended inside quoted string value\n");
+ return 0;
+ break;
+ case '\\':
+ /* end of string? */
+ if (!*(*auth)++) {
+ MSG("auth.c: "
+ "auth string ended inside quoted string value "
+ "immediately after \\\n");
+ return 0;
+ }
+ /* fall through to the next case */
+ default:
+ size++;
+ break;
+ }
+ }
+}
+
+/*
+ * Parse a token-value pair.
+ * Return: 1 if the parse succeeds, 0 otherwise.
+ */
+static int Auth_parse_token_value(AuthParse_t *auth_parse, char **auth)
+{
+ char *token;
+ int token_size, set_realm;
+ static const char realm_token[] = "realm";
+
+ /* parse a token */
+ token = *auth;
+ token_size = 0;
+ while (Auth_is_token_char(**auth)) {
+ (*auth)++;
+ token_size++;
+ }
+ if (token_size == 0) {
+ MSG("auth.c: Auth_parse_token_value: "
+ "missing Basic auth token\n");
+ return 0;
+ }
+
+ /* skip space characters */
+ while (**auth == ' ')
+ (*auth)++;
+
+ /* parse the '=' */
+ switch (*(*auth)++) {
+ case '=':
+ break;
+ case '\0':
+ case ',':
+ MSG("auth.c: Auth_parse_token_value: "
+ "missing Basic auth token value\n");
+ return 0;
+ break;
+ default:
+ MSG("auth.c: Auth_parse_token_value: "
+ "garbage after Basic auth token\n");
+ return 0;
+ break;
+ }
+
+ /* skip space characters */
+ while (**auth == ' ')
+ (*auth)++;
+
+ /* is this value the realm? */
+ set_realm =
+ auth_parse->realm == NULL &&
+ dStrncasecmp(realm_token,token,token_size) == 0 &&
+ strlen(realm_token) == (size_t)token_size;
+
+ return Auth_parse_quoted_string(auth_parse, set_realm, auth);
+}
+
+static void Auth_parse_auth_basic(AuthParse_t *auth_parse, char **auth)
+{
+ int token_value_pairs_found;
+
+ /* parse comma-separated token-value pairs */
+ token_value_pairs_found = 0;
+ while (1) {
+ /* skip space and comma characters */
+ while (**auth == ' ' || **auth == ',')
+ (*auth)++;
+ /* end of string? */
+ if (!**auth)
+ break;
+ /* parse token-value pair */
+ if (!Auth_parse_token_value(auth_parse, auth))
+ break;
+ token_value_pairs_found = 1;
+ }
+
+ if (!token_value_pairs_found) {
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "missing Basic auth token-value pairs\n");
+ return;
+ }
+
+ if (!auth_parse->realm) {
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "missing Basic auth realm\n");
+ return;
+ }
+}
+
+static void Auth_parse_auth(AuthParse_t *auth_parse, char *auth)
+{
+ _MSG("auth.c: Auth_parse_auth: auth = '%s'\n", auth);
+ if (dStrncasecmp(auth, "Basic ", 6) == 0) {
+ auth += 6;
+ Auth_parse_auth_basic(auth_parse, &auth);
+ } else {
+ MSG("auth.c: Auth_parse_auth: "
+ "unknown authorization scheme: auth = {%s}\n",
+ auth);
+ }
+}
+
+/*
+ * Return the host that contains a URL, or NULL if there is no such host.
+ */
+static AuthHost_t *Auth_host_by_url(const DilloUrl *url)
+{
+ AuthHost_t *host;
+ int i;
+
+ for (i = 0; (host = dList_nth_data(auth_hosts, i)); i++)
+ if (((dStrcasecmp(URL_SCHEME(url), host->scheme) == 0) &&
+ (dStrcasecmp(URL_AUTHORITY(url), host->authority) == 0)))
+ return host;
+
+ return NULL;
+}
+
+/*
+ * Search all realms for the one with the given name.
+ */
+static AuthRealm_t *Auth_realm_by_name(const AuthHost_t *host,
+ const char *name)
+{
+ AuthRealm_t *realm;
+ int i;
+
+ for (i = 0; (realm = dList_nth_data(host->realms, i)); i++)
+ if (strcmp(realm->name,name) == 0)
+ return realm;
+
+ return NULL;
+}
+
+/*
+ * Search all realms for the one with the best-matching path.
+ */
+static AuthRealm_t *Auth_realm_by_path(const AuthHost_t *host,
+ const char *path)
+{
+ AuthRealm_t *realm_best, *realm;
+ int i, j;
+ int match_length;
+
+ match_length = 0;
+ realm_best = NULL;
+ for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) {
+ char *realm_path;
+
+ for (j = 0; (realm_path = dList_nth_data(realm->paths, j)); j++) {
+ int realm_path_length = strlen(realm_path);
+ if (Auth_path_is_inside(path, realm_path, realm_path_length) &&
+ !(realm_best && match_length >= realm_path_length)) {
+ realm_best = realm;
+ match_length = realm_path_length;
+ }
+ }
+ }
+
+ return realm_best;
+}
+
+static int Auth_realm_includes_path(const AuthRealm_t *realm, const char *path)
+{
+ int i;
+ char *realm_path;
+
+ for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++)
+ if (Auth_path_is_inside(path, realm_path, strlen(realm_path)))
+ return 1;
+
+ return 0;
+}
+
+static void Auth_realm_add_path(AuthRealm_t *realm, const char *path)
+{
+ int len, i;
+ char *realm_path, *n_path;
+
+ n_path = dStrdup(path);
+ len = strlen(n_path);
+
+ /* remove trailing '/' */
+ if (len && n_path[len - 1] == '/')
+ n_path[--len] = 0;
+
+ /* delete existing paths that are inside the new one */
+ for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++) {
+ if (Auth_path_is_inside(realm_path, path, len)) {
+ dList_remove_fast(realm->paths, realm_path);
+ dFree(realm_path);
+ i--; /* reconsider this slot */
+ }
+ }
+
+ dList_append(realm->paths, n_path);
+}
+
+/*
+ * Return the authorization header for an HTTP query.
+ */
+const char *a_Auth_get_auth_str(const DilloUrl *url)
+{
+ AuthHost_t *host;
+ AuthRealm_t *realm;
+
+ return
+ ((host = Auth_host_by_url(url)) &&
+ (realm = Auth_realm_by_path(host, URL_PATH(url)))) ?
+ realm->authorization : NULL;
+}
+
+/*
+ * Determine whether the user needs to authenticate.
+ */
+static int Auth_do_auth_required(const char *realm_name, const DilloUrl *url)
+{
+ /*
+ * TO DO: I dislike the way that this code must decide whether we
+ * sent authentication during the request and trust us to resend it
+ * after the reload. Could it be more robust if every DilloUrl
+ * recorded its authentication, and whether it was accepted? (JCH)
+ */
+
+ AuthHost_t *host;
+ AuthRealm_t *realm;
+
+ /*
+ * The size of the following comments reflects the concerns in the
+ * TO DO at the top of this function. It should not be so hard to
+ * explain why code is correct! (JCH)
+ */
+
+ /*
+ * If we have authentication but did not send it (because we did
+ * not know this path was in the realm) then we update the realm.
+ * We do not re-authenticate because our authentication is probably
+ * OK. Thanks to the updated realm the forthcoming reload will
+ * make us send the authentication. If our authentication is not
+ * OK the server will challenge us again after the reload and then
+ * we will re-authenticate.
+ */
+ if ((host = Auth_host_by_url(url)) &&
+ (realm = Auth_realm_by_name(host, realm_name)) &&
+ (!Auth_realm_includes_path(realm, URL_PATH(url)))) {
+ _MSG("Auth_do_auth_required: updating realm '%s' with URL '%s'\n",
+ realm_name, URL_STR(url));
+ Auth_realm_add_path(realm, URL_PATH(url));
+ return 0;
+ }
+
+ /*
+ * Either we had no authentication or we sent it and the server
+ * rejected it, so we must re-authenticate.
+ */
+ return 1;
+}
+
+static void Auth_do_auth_dialog_cb(const char *user, const char *password,
+ void *vData)
+{
+ AuthDialogData_t *data;
+ AuthHost_t *host;
+ AuthRealm_t *realm;
+ char *user_password, *response, *authorization, *authorization_old;
+
+ data = (AuthDialogData_t *)vData;
+
+ /* find or create the host */
+ if (!(host = Auth_host_by_url(data->url))) {
+ /* create a new host */
+ host = dNew(AuthHost_t, 1);
+ host->scheme = dStrdup(URL_SCHEME(data->url));
+ host->authority = dStrdup(URL_AUTHORITY(data->url));
+ host->realms = dList_new(1);
+ dList_append(auth_hosts, host);
+ }
+
+ /* find or create the realm */
+ if (!(realm = Auth_realm_by_name(host, data->realm_name))) {
+ /* create a new realm */
+ realm = dNew(AuthRealm_t, 1);
+ realm->name = dStrdup(data->realm_name);
+ realm->paths = dList_new(1);
+ realm->authorization = NULL;
+ dList_append(host->realms, realm);
+ }
+
+ Auth_realm_add_path(realm, URL_PATH(data->url));
+
+ /* create and set the authorization */
+ user_password = dStrconcat(user, ":", password, NULL);
+ response = a_Misc_encode_base64(user_password);
+ authorization =
+ dStrconcat("Authorization: Basic ", response, "\r\n", NULL);
+ authorization_old = realm->authorization;
+ realm->authorization = authorization;
+ dFree(authorization_old);
+ dFree(user_password);
+ dFree(response);
+}
+
+static int Auth_do_auth_dialog(const char *realm, const DilloUrl *url)
+{
+ int ret;
+ char *message;
+ AuthDialogData_t *data;
+
+ _MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", realm);
+ message = dStrconcat("Enter a user and password for \"",
+ realm, "\".", NULL);
+ data = dNew(AuthDialogData_t, 1);
+ data->realm_name = dStrdup(realm);
+ data->url = a_Url_dup(url);
+ ret = a_Dialog_user_password(message, Auth_do_auth_dialog_cb, data);
+ dFree(message);
+ dFree((void*)data->realm_name);
+ a_Url_free((void*)data->url);
+ dFree(data);
+ return ret;
+}
+
+/*
+ * Do authorization for an auth string.
+ */
+static int Auth_do_auth(char *auth, const DilloUrl *url)
+{
+ int reload;
+ AuthParse_t *auth_parse;
+
+ _MSG("auth.c: Auth_do_auth: auth={%s}\n", auth);
+ reload = 0;
+ auth_parse = Auth_parse_new();
+ Auth_parse_auth(auth_parse, auth);
+ if (auth_parse->ok)
+ reload =
+ Auth_do_auth_required(auth_parse->realm, url) ?
+ Auth_do_auth_dialog(auth_parse->realm, url)
+ : 1;
+ Auth_parse_free(auth_parse);
+
+ return reload;
+}
+
+/*
+ * Do authorization for a set of auth strings.
+ */
+int a_Auth_do_auth(Dlist *auths, const DilloUrl *url)
+{
+ int reload, i;
+ char *auth;
+
+ reload = 0;
+ for (i = 0; (auth = dList_nth_data(auths, i)); ++i)
+ if (Auth_do_auth(auth, url))
+ reload = 1;
+
+ return reload;
+}
+
diff --git a/src/auth.h b/src/auth.h
new file mode 100644
index 00000000..5f96f642
--- /dev/null
+++ b/src/auth.h
@@ -0,0 +1,19 @@
+#ifndef __AUTH_H__
+#define __AUTH_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "url.h"
+
+
+const char *a_Auth_get_auth_str(const DilloUrl *request_url);
+int a_Auth_do_auth(Dlist *auth_string, const DilloUrl *url);
+void a_Auth_init(void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* !__AUTH_H__ */
diff --git a/src/bookmark.c b/src/bookmark.c
index 588e51f8..9a594789 100644
--- a/src/bookmark.c
+++ b/src/bookmark.c
@@ -9,10 +9,7 @@
* (at your option) any later version.
*/
-#include <errno.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include "msg.h"
#include "history.h"
diff --git a/src/bw.c b/src/bw.c
index 27f3066c..7f4785bb 100644
--- a/src/bw.c
+++ b/src/bw.c
@@ -13,6 +13,7 @@
#include "bw.h"
+#include "msg.h"
#include "list.h"
#include "capi.h"
#include "uicmd.hh"
@@ -58,11 +59,14 @@ BrowserWindow *a_Bw_new()
bw->nav_expect_url = NULL;
bw->redirect_level = 0;
+ bw->meta_refresh_status = 0;
+ bw->meta_refresh_url = NULL;
bw->RootClients = dList_new(8);
bw->ImageClients = dList_new(8);
bw->NumImages = 0;
bw->NumImagesGot = 0;
+ bw->NumPendingStyleSheets = 0;
bw->PageUrls = dList_new(8);
bw->Docs = dList_new(8);
@@ -99,6 +103,8 @@ void a_Bw_free(BrowserWindow *bw)
dFree(dList_nth_data(bw->nav_stack, j));
dList_free(bw->nav_stack);
+ a_Url_free(bw->meta_refresh_url);
+
dStr_free(bw->page_bugs, 1);
dFree(bw);
break;
@@ -176,7 +182,7 @@ void a_Bw_stop_clients(BrowserWindow *bw, int flags)
if (flags & BW_Root) {
/* Remove root clients */
while ((data = dList_nth_data(bw->RootClients, 0))) {
- a_Capi_stop_client(VOIDP2INT(data), (flags & Bw_Force));
+ a_Capi_stop_client(VOIDP2INT(data), (flags & BW_Force));
dList_remove_fast(bw->RootClients, data);
}
}
@@ -184,7 +190,7 @@ void a_Bw_stop_clients(BrowserWindow *bw, int flags)
if (flags & BW_Img) {
/* Remove image clients */
while ((data = dList_nth_data(bw->ImageClients, 0))) {
- a_Capi_stop_client(VOIDP2INT(data), (flags & Bw_Force));
+ a_Capi_stop_client(VOIDP2INT(data), (flags & BW_Force));
dList_remove_fast(bw->ImageClients, data);
}
}
@@ -216,6 +222,38 @@ void a_Bw_add_doc(BrowserWindow *bw, void *vdoc)
}
/*
+ * Get current document.
+ */
+void *a_Bw_get_current_doc(BrowserWindow *bw)
+{
+ void *doc = NULL;
+ int len = dList_length(bw->Docs);
+
+ if (len == 1)
+ doc = dList_nth_data(bw->Docs, 0);
+ else if (len > 1)
+ MSG("a_Bw_get_current_doc() multiple docs not implemented\n");
+
+ return doc;
+}
+
+/*
+ * Get document by URL.
+ *
+ * This is currently used by popup menus that need to ensure that the
+ * page has not changed while the menu was popped up.
+ */
+void *a_Bw_get_url_doc(BrowserWindow *bw, const DilloUrl *url)
+{
+ void *doc = NULL;
+
+ if (url && dList_find_custom(bw->PageUrls, url, (dCompareFunc)a_Url_cmp)) {
+ doc = a_Bw_get_current_doc(bw);
+ }
+ return doc;
+}
+
+/*
* Remove a document from the bw's list
*/
void a_Bw_remove_doc(BrowserWindow *bw, void *vdoc)
@@ -253,6 +291,9 @@ void a_Bw_cleanup(BrowserWindow *bw)
/* Zero image-progress data */
bw->NumImages = 0;
bw->NumImagesGot = 0;
+
+ /* Zero stylesheet counter */
+ bw->NumPendingStyleSheets = 0;
}
/*--------------------------------------------------------------------------*/
diff --git a/src/bw.h b/src/bw.h
index f0d8dd7d..4b915b5c 100644
--- a/src/bw.h
+++ b/src/bw.h
@@ -1,8 +1,6 @@
#ifndef __BW_H__
#define __BW_H__
-#include <sys/types.h>
-
#include "url.h" /* for DilloUrl */
/*
@@ -10,7 +8,7 @@
*/
#define BW_Root (1) /* Root URLs */
#define BW_Img (2) /* Image URLs */
-#define Bw_Force (4) /* Stop connection too */
+#define BW_Force (4) /* Stop connection too */
typedef struct _BrowserWindow BrowserWindow;
@@ -38,6 +36,8 @@ struct _BrowserWindow
int NumImages;
/* Number of images already loaded */
int NumImagesGot;
+ /* Number of not yet arrived style sheets */
+ int NumPendingStyleSheets;
/* List of all Urls requested by this page (and its types) */
Dlist *PageUrls;
@@ -57,8 +57,11 @@ struct _BrowserWindow
* redirection loops (accounts for WEB_RootUrl only) */
int redirect_level;
- /* TODO: maybe this fits better in the linkblock.
- * Although having it here avoids having a signal for handling it. */
+ /* Url for zero-delay redirections in the META element */
+ int meta_refresh_status;
+ DilloUrl *meta_refresh_url;
+
+ /* HTML-bugs detected at parse time */
int num_page_bugs;
Dstr *page_bugs;
};
@@ -80,10 +83,13 @@ int a_Bw_remove_client(BrowserWindow *bw, int ClientKey);
void a_Bw_close_client(BrowserWindow *bw, int ClientKey);
void a_Bw_stop_clients(BrowserWindow *bw, int flags);
void a_Bw_add_doc(BrowserWindow *bw, void *vdoc);
+void *a_Bw_get_current_doc(BrowserWindow *bw);
+void *a_Bw_get_url_doc(BrowserWindow *bw, const DilloUrl *Url);
void a_Bw_remove_doc(BrowserWindow *bw, void *vdoc);
void a_Bw_add_url(BrowserWindow *bw, const DilloUrl *Url);
void a_Bw_cleanup(BrowserWindow *bw);
+typedef void (*BwCallback_t)(BrowserWindow *bw, const void *data);
#ifdef __cplusplus
}
diff --git a/src/cache.c b/src/cache.c
index 9ddb0abf..fcd27a05 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -16,11 +16,8 @@
#include <ctype.h> /* for tolower */
#include <sys/types.h>
-#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
#include "msg.h"
#include "IO/Url.h"
@@ -32,6 +29,7 @@
#include "misc.h"
#include "capi.h"
#include "decode.h"
+#include "auth.h"
#include "timeout.hh"
#include "uicmd.hh"
@@ -52,8 +50,10 @@ typedef struct {
char *TypeDet; /* MIME type string (detected from data) */
char *TypeHdr; /* MIME type string as from the HTTP Header */
char *TypeMeta; /* MIME type string from META HTTP-EQUIV */
+ char *TypeNorm; /* MIME type string normalized */
Dstr *Header; /* HTTP header */
const DilloUrl *Location; /* New URI for redirects */
+ Dlist *Auth; /* Authentication fields */
Dstr *Data; /* Pointer to raw data */
Dstr *UTF8Data; /* Data after charset translation */
int DataRefcount; /* Reference count */
@@ -85,9 +85,10 @@ static uint_t DelayedQueueIdleId = 0;
/*
* Forward declarations
*/
-static void Cache_process_queue(CacheEntry_t *entry);
+static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry);
static void Cache_delayed_process_queue(CacheEntry_t *entry);
-
+static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw);
+static void Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds);
/*
* Determine if two cache entries are equal (used by CachedURLs)
@@ -123,7 +124,7 @@ void a_Cache_init(void)
{
DilloUrl *url = a_Url_new("about:splash", NULL);
Dstr *ds = dStr_new(AboutSplash);
- a_Cache_entry_inject(url, ds);
+ Cache_entry_inject(url, ds);
dStr_free(ds, 1);
a_Url_free(url);
}
@@ -132,18 +133,6 @@ void a_Cache_init(void)
/* Client operations ------------------------------------------------------ */
/*
- * Make a unique primary-key for cache clients
- */
-static int Cache_client_make_key(void)
-{
- static int ClientKey = 0; /* Provide a primary key for each client */
-
- if (++ClientKey < 0)
- ClientKey = 1;
- return ClientKey;
-}
-
-/*
* Add a client to ClientQueue.
* - Every client-field is just a reference (except 'Web').
* - Return a unique number for identifying the client.
@@ -151,14 +140,18 @@ static int Cache_client_make_key(void)
static int Cache_client_enqueue(const DilloUrl *Url, DilloWeb *Web,
CA_Callback_t Callback, void *CbData)
{
- int ClientKey;
+ static int ClientKey = 0; /* Provide a primary key for each client */
CacheClient_t *NewClient;
+ if (++ClientKey <= 0)
+ ClientKey = 1;
+
NewClient = dNew(CacheClient_t, 1);
- ClientKey = Cache_client_make_key();
NewClient->Key = ClientKey;
NewClient->Url = Url;
+ NewClient->Version = 0;
NewClient->Buf = NULL;
+ NewClient->BufSize = 0;
NewClient->Callback = Callback;
NewClient->CbData = CbData;
NewClient->Web = Web;
@@ -204,8 +197,10 @@ static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url)
NewEntry->TypeDet = NULL;
NewEntry->TypeHdr = NULL;
NewEntry->TypeMeta = NULL;
+ NewEntry->TypeNorm = NULL;
NewEntry->Header = dStr_new("");
NewEntry->Location = NULL;
+ NewEntry->Auth = NULL;
NewEntry->Data = dStr_sized_new(8*1024);
NewEntry->UTF8Data = NULL;
NewEntry->DataRefcount = 0;
@@ -272,7 +267,7 @@ static CacheEntry_t *Cache_entry_add(const DilloUrl *Url)
* Inject full page content directly into the cache.
* Used for "about:splash". May be used for "about:cache" too.
*/
-void a_Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds)
+static void Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds)
{
CacheEntry_t *entry;
@@ -288,6 +283,18 @@ void a_Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds)
}
/*
+ * Free Authentication fields.
+ */
+static void Cache_auth_free(Dlist *auth)
+{
+ int i;
+ void *auth_field;
+ for (i = 0; (auth_field = dList_nth_data(auth, i)); ++i)
+ dFree(auth_field);
+ dList_free(auth);
+}
+
+/*
* Free the components of a CacheEntry_t struct.
*/
static void Cache_entry_free(CacheEntry_t *entry)
@@ -296,8 +303,10 @@ static void Cache_entry_free(CacheEntry_t *entry)
dFree(entry->TypeDet);
dFree(entry->TypeHdr);
dFree(entry->TypeMeta);
+ dFree(entry->TypeNorm);
dStr_free(entry->Header, TRUE);
a_Url_free((DilloUrl *)entry->Location);
+ Cache_auth_free(entry->Auth);
dStr_free(entry->Data, 1);
dStr_free(entry->UTF8Data, 1);
if (entry->CharsetDecoder)
@@ -353,14 +362,13 @@ void a_Cache_entry_remove_by_url(DilloUrl *url)
* Try finding the url in the cache. If it hits, send the cache contents
* from there. If it misses, set up a new connection.
*
- * - 'Web' is an auxiliar data structure with misc. parameters.
+ * - 'Web' is an auxiliary data structure with misc. parameters.
* - 'Call' is the callback that receives the data
* - 'CbData' is custom data passed to 'Call'
* Note: 'Call' and/or 'CbData' can be NULL, in that case they get set
* later by a_Web_dispatch_by_type, based on content/type and 'Web' data.
*
* Return value: A primary key for identifying the client,
- * 0 if the client is aborted in the process.
*/
int a_Cache_open_url(void *web, CA_Callback_t Call, void *CbData)
{
@@ -394,6 +402,15 @@ int a_Cache_open_url(void *web, CA_Callback_t Call, void *CbData)
*/
uint_t a_Cache_get_flags(const DilloUrl *url)
{
+ CacheEntry_t *entry = Cache_entry_search(url);
+ return (entry ? entry->Flags : 0);
+}
+
+/*
+ * Get cache entry status (following redirections).
+ */
+uint_t a_Cache_get_flags_with_redirection(const DilloUrl *url)
+{
CacheEntry_t *entry = Cache_entry_search_with_redirect(url);
return (entry ? entry->Flags : 0);
}
@@ -406,7 +423,9 @@ static void Cache_ref_data(CacheEntry_t *entry)
if (entry) {
entry->DataRefcount++;
_MSG("DataRefcount++: %d\n", entry->DataRefcount);
- if (entry->CharsetDecoder && entry->DataRefcount == 1) {
+ if (entry->CharsetDecoder &&
+ (!entry->UTF8Data || entry->DataRefcount == 1)) {
+ dStr_free(entry->UTF8Data, 1);
entry->UTF8Data = a_Decode_process(entry->CharsetDecoder,
entry->Data->str,
entry->Data->len);
@@ -440,8 +459,8 @@ static void Cache_unref_data(CacheEntry_t *entry)
*/
static const char *Cache_current_content_type(CacheEntry_t *entry)
{
- return entry->TypeMeta ? entry->TypeMeta : entry->TypeHdr ? entry->TypeHdr :
- entry->TypeDet;
+ return entry->TypeNorm ? entry->TypeNorm : entry->TypeMeta ? entry->TypeMeta
+ : entry->TypeHdr ? entry->TypeHdr : entry->TypeDet;
}
/*
@@ -464,44 +483,53 @@ static Dstr *Cache_data(CacheEntry_t *entry)
/*
* Change Content-Type for cache entry found by url.
+ * from = { "http" | "meta" }
* Return new content type.
*/
const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype,
- bool_t force)
+ const char *from)
{
const char *curr;
- CacheEntry_t *entry = Cache_entry_search_with_redirect(url);
+ char *major, *minor, *charset;
+ CacheEntry_t *entry = Cache_entry_search(url);
- if (!entry)
- return NULL;
+ dReturn_val_if_fail (entry != NULL, NULL);
- curr = Cache_current_content_type(entry);
- if (entry->TypeMeta && (force == FALSE)) {
- /* it's already been set */
- return curr;
- }
-
- if (a_Misc_content_type_cmp(curr, ctype)) {
- char *charset;
+ _MSG("a_Cache_set_content_type {%s} {%s}\n", ctype, URL_STR(url));
- dFree(entry->TypeMeta);
- curr = entry->TypeMeta = dStrdup(ctype);
-
- if (entry->CharsetDecoder)
- a_Decode_free(entry->CharsetDecoder);
- a_Misc_parse_content_type(ctype, NULL, NULL, &charset);
- entry->CharsetDecoder = a_Decode_charset_init(charset);
- dFree(charset);
+ curr = Cache_current_content_type(entry);
+ if (entry->TypeMeta || (*from == 'h' && entry->TypeHdr) ) {
+ /* Type is already been set. Do nothing.
+ * BTW, META overrides TypeHdr */
+ } else {
+ if (*from == 'h') {
+ /* Content-Type from HTTP header */
+ entry->TypeHdr = dStrdup(ctype);
+ } else {
+ /* Content-Type from META */
+ entry->TypeMeta = dStrdup(ctype);
+ }
+ if (a_Misc_content_type_cmp(curr, ctype)) {
+ /* ctype gives one different from current */
+ a_Misc_parse_content_type(ctype, &major, &minor, &charset);
+ if (*from == 'm' && charset &&
+ ((!major || !*major) && (!minor || !*minor))) {
+ /* META only gives charset; use detected MIME type too */
+ entry->TypeNorm = dStrconcat(entry->TypeDet, ctype, NULL);
+ }
+ if (charset) {
+ if (entry->CharsetDecoder)
+ a_Decode_free(entry->CharsetDecoder);
+ entry->CharsetDecoder = a_Decode_charset_init(charset);
+ curr = Cache_current_content_type(entry);
- dStr_free(entry->UTF8Data, 1);
- if (entry->CharsetDecoder && entry->DataRefcount > 0)
- entry->UTF8Data = a_Decode_process(entry->CharsetDecoder,
- entry->Data->str,
- entry->Data->len);
- else
- entry->UTF8Data = NULL;
+ /* Invalidate UTF8Data */
+ dStr_free(entry->UTF8Data, 1);
+ entry->UTF8Data = NULL;
+ }
+ dFree(major); dFree(minor); dFree(charset);
+ }
}
-
return curr;
}
@@ -513,8 +541,9 @@ int a_Cache_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
{
CacheEntry_t *entry = Cache_entry_search_with_redirect(Url);
if (entry) {
+ Dstr *data;
Cache_ref_data(entry);
- Dstr *data = Cache_data(entry);
+ data = Cache_data(entry);
*PBuf = data->str;
*BufSize = data->len;
} else {
@@ -556,14 +585,16 @@ static char *Cache_parse_field(const char *header, const char *fieldname)
}
i += j;
- while (header[i] == ' ') i++;
if (header[i] == ':') {
/* Field found! */
while (header[++i] == ' ' || header[i] == '\t');
for (j = 0; header[i + j] != '\n'; j++);
+ while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t'))
+ j--;
field = dStrndup(header + i, j);
return field;
}
+ while (header[i] != '\n') i++;
}
return NULL;
}
@@ -590,13 +621,16 @@ static Dlist *Cache_parse_multiple_fields(const char *header,
}
i += j;
- for ( ; header[i] == ' '; i++);
if (header[i] == ':') {
/* Field found! */
while (header[++i] == ' ' || header[i] == '\t');
for (j = 0; header[i + j] != '\n'; j++);
+ while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t'))
+ j--;
field = dStrndup(header + i, j);
dList_append(fields, field);
+ } else {
+ while (header[i] != '\n') i++;
}
}
@@ -614,15 +648,16 @@ static Dlist *Cache_parse_multiple_fields(const char *header,
static void Cache_parse_header(CacheEntry_t *entry)
{
char *header = entry->Header->str;
- char *Length, *Type, *location_str, *encoding, *charset;
+ char *Length, *Type, *location_str, *encoding;
#ifndef DISABLE_COOKIES
Dlist *Cookies;
#endif
- DilloUrl *location_url;
Dlist *warnings;
void *data;
int i;
+ _MSG("Cache_parse_header\n");
+
if (entry->Header->len > 12) {
if (header[9] == '1' && header[10] == '0' && header[11] == '0') {
/* 100: Continue. The "real" header has not come yet. */
@@ -634,26 +669,31 @@ static void Cache_parse_header(CacheEntry_t *entry)
}
if (header[9] == '3' && header[10] == '0') {
/* 30x: URL redirection */
- entry->Flags |= CA_Redirect;
- if (header[11] == '1')
- entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */
- else if (header[11] == '2')
- entry->Flags |= CA_TempRedirect; /* 302 Temporary Redirect */
-
- location_str = Cache_parse_field(header, "Location");
- location_url = a_Url_new(location_str, URL_STR_(entry->Url));
- if (URL_FLAGS(location_url) & (URL_Post + URL_Get) &&
- dStrcasecmp(URL_SCHEME(location_url), "dpi") == 0 &&
- dStrcasecmp(URL_SCHEME(entry->Url), "dpi") != 0) {
- /* Forbid dpi GET and POST from non dpi-generated urls */
- MSG("Redirection Denied! '%s' -> '%s'\n",
- URL_STR(entry->Url), URL_STR(location_url));
- a_Url_free(location_url);
- } else {
- entry->Location = location_url;
+ if ((location_str = Cache_parse_field(header, "Location"))) {
+ DilloUrl *location_url;
+
+ entry->Flags |= CA_Redirect;
+ if (header[11] == '1')
+ entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */
+ else if (header[11] == '2')
+ entry->Flags |= CA_TempRedirect; /* 302 Temporary Redirect */
+
+ location_url = a_Url_new(location_str, URL_STR_(entry->Url));
+ if (URL_FLAGS(location_url) & (URL_Post + URL_Get) &&
+ dStrcasecmp(URL_SCHEME(location_url), "dpi") == 0 &&
+ dStrcasecmp(URL_SCHEME(entry->Url), "dpi") != 0) {
+ /* Forbid dpi GET and POST from non dpi-generated urls */
+ MSG("Redirection Denied! '%s' -> '%s'\n",
+ URL_STR(entry->Url), URL_STR(location_url));
+ a_Url_free(location_url);
+ } else {
+ entry->Location = location_url;
+ }
+ dFree(location_str);
}
- dFree(location_str);
-
+ } else if (strncmp(header + 9, "401", 3) == 0) {
+ entry->Auth =
+ Cache_parse_multiple_fields(header, "WWW-Authenticate");
} else if (strncmp(header + 9, "404", 3) == 0) {
entry->Flags |= CA_NotFound;
}
@@ -667,16 +707,22 @@ static void Cache_parse_header(CacheEntry_t *entry)
dList_free(warnings);
}
+ /*
+ * Get Transfer-Encoding and initialize decoder
+ */
+ encoding = Cache_parse_field(header, "Transfer-Encoding");
+ entry->TransferDecoder = a_Decode_transfer_init(encoding);
+
+
if ((Length = Cache_parse_field(header, "Content-Length")) != NULL) {
- char *tmp;
- if ((tmp = Cache_parse_field(header, "Transfer-Encoding"))) {
+ if (encoding) {
/*
- * BUG: Should test for _presence_ of headers, not whether they
- * have content.
+ * If Transfer-Encoding is present, Content-Length must be ignored.
+ * If the Transfer-Encoding is non-identity, it is an error.
*/
- MSG_HTTP("Both Content-Length and Transfer-Encoding headers"
- " received.\n");
- dFree(tmp);
+ if (dStrcasecmp(encoding, "identity"))
+ MSG_HTTP("Content-Length and non-identity Transfer-Encoding "
+ "headers both present.\n");
} else {
entry->Flags |= CA_GotLength;
entry->ExpectedSize = MAX(strtol(Length, NULL, 10), 0);
@@ -684,27 +730,21 @@ static void Cache_parse_header(CacheEntry_t *entry)
dFree(Length);
}
+ dFree(encoding); /* free Transfer-Encoding */
+
#ifndef DISABLE_COOKIES
- /* BUG: If a server feels like mixing Set-Cookie2 and Set-Cookie
- * responses which aren't identical, then we have a problem. I don't
- * know if that is a real issue though. */
- if ((Cookies = Cache_parse_multiple_fields(header, "Set-Cookie2")) ||
- (Cookies = Cache_parse_multiple_fields(header, "Set-Cookie"))) {
- a_Cookies_set(Cookies, entry->Url);
+ if ((Cookies = Cache_parse_multiple_fields(header, "Set-Cookie"))) {
+ char *server_date = Cache_parse_field(header, "Date");
+
+ a_Cookies_set(Cookies, entry->Url, server_date);
for (i = 0; (data = dList_nth_data(Cookies, i)); ++i)
dFree(data);
dList_free(Cookies);
+ dFree(server_date);
}
#endif /* !DISABLE_COOKIES */
/*
- * Get Transfer-Encoding and initialize decoder
- */
- encoding = Cache_parse_field(header, "Transfer-Encoding");
- entry->TransferDecoder = a_Decode_transfer_init(encoding);
- dFree(encoding);
-
- /*
* Get Content-Encoding and initialize decoder
*/
encoding = Cache_parse_field(header, "Content-Encoding");
@@ -721,28 +761,17 @@ static void Cache_parse_header(CacheEntry_t *entry)
dStr_free(entry->Data, 1);
entry->Data = dStr_sized_new(MIN(entry->ExpectedSize, MAX_INIT_BUF));
}
- Cache_ref_data(entry);
/* Get Content-Type */
- if ((Type = Cache_parse_field(header, "Content-Type")) == NULL) {
- if (!((entry->Flags & CA_GotLength) && (entry->ExpectedSize == 0))) {
- /* unless the server sent Content-Length: 0 */
- MSG_HTTP("Server didn't send Content-Type in header.\n");
- }
- } else {
- entry->TypeHdr = Type;
- _MSG("Content-Type {%s} {%s}\n", Type, URL_STR(entry->Url));
- /* This Content-Type is not trusted. It's checked against real data
- * in Cache_process_queue(); only then CA_GotContentType becomes true.
- */
- a_Misc_parse_content_type(Type, NULL, NULL, &charset);
- if (charset) {
- entry->CharsetDecoder = a_Decode_charset_init(charset);
- if (entry->CharsetDecoder)
- entry->UTF8Data = dStr_new("");
- dFree(charset);
- }
+ if ((Type = Cache_parse_field(header, "Content-Type"))) {
+ /* This HTTP Content-Type is not trusted. It's checked against real data
+ * in Cache_process_queue(); only then CA_GotContentType becomes true. */
+ a_Cache_set_content_type(entry->Url, Type, "http");
+ _MSG("TypeHdr {%s} {%s}\n", Type, URL_STR(entry->Url));
+ _MSG("TypeMeta {%s}\n", entry->TypeMeta);
+ dFree(Type);
}
+ Cache_ref_data(entry);
}
/*
@@ -762,7 +791,7 @@ static int Cache_get_header(CacheEntry_t *entry,
continue;
if (N == 1 && (buf[i] == ' ' || buf[i] == '\t')) {
/* unfold multiple-line header */
- MSG("Multiple-line header!\n");
+ _MSG("Multiple-line header!\n");
dStr_erase(hdr, hdr->len - 1, 1);
}
N = (buf[i] == '\n') ? N + 1 : 0;
@@ -791,15 +820,64 @@ static int Cache_get_header(CacheEntry_t *entry,
void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
const DilloUrl *Url)
{
- int offset = 0;
- int len;
+ int offset, len;
const char *str;
+ Dstr *dstr1, *dstr2, *dstr3;
CacheEntry_t *entry = Cache_entry_search(Url);
/* Assert a valid entry (not aborted) */
dReturn_if_fail (entry != NULL);
- if (Op == IOClose) {
+ _MSG("__a_Cache_process_dbuf__\n");
+
+ if (Op == IORead) {
+ /*
+ * Cache_get_header() will set CA_GotHeader if it has a full header, and
+ * Cache_parse_header() will unset it if the header ends being
+ * merely an informational response from the server (i.e., 100 Continue)
+ */
+ for (offset = 0; !(entry->Flags & CA_GotHeader) &&
+ (len = Cache_get_header(entry, buf + offset, buf_size - offset));
+ Cache_parse_header(entry) ) {
+ offset += len;
+ }
+
+ if (entry->Flags & CA_GotHeader) {
+ str = buf + offset;
+ len = buf_size - offset;
+ entry->TransferSize += len;
+ dstr1 = dstr2 = dstr3 = NULL;
+
+ /* Decode arrived data (<= 3 stages) */
+ if (entry->TransferDecoder) {
+ dstr1 = a_Decode_process(entry->TransferDecoder, str, len);
+ str = dstr1->str;
+ len = dstr1->len;
+ }
+ if (entry->ContentDecoder) {
+ dstr2 = a_Decode_process(entry->ContentDecoder, str, len);
+ str = dstr2->str;
+ len = dstr2->len;
+ }
+ dStr_append_l(entry->Data, str, len);
+ if (entry->CharsetDecoder && entry->UTF8Data) {
+ dstr3 = a_Decode_process(entry->CharsetDecoder, str, len);
+ dStr_append_l(entry->UTF8Data, dstr3->str, dstr3->len);
+ }
+ dStr_free(dstr1, 1);
+ dStr_free(dstr2, 1);
+ dStr_free(dstr3, 1);
+
+ if (entry->Data->len)
+ entry->Flags &= ~CA_IsEmpty;
+
+ entry = Cache_process_queue(entry);
+ }
+ } else if (Op == IOClose) {
+ if ((entry->ExpectedSize || entry->TransferSize) &&
+ entry->TypeHdr == NULL) {
+ MSG_HTTP("Message with a body lacked Content-Type header.\n");
+ }
if ((entry->Flags & CA_GotLength) &&
(entry->ExpectedSize != entry->TransferSize)) {
MSG_HTTP("Content-Length does NOT match message body,\n"
@@ -818,65 +896,16 @@ void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
entry->ContentDecoder = NULL;
}
dStr_fit(entry->Data); /* fit buffer size! */
- Cache_process_queue(entry);
- if (entry->Flags & CA_GotHeader) {
- Cache_unref_data(entry);
+
+ if ((entry = Cache_process_queue(entry))) {
+ if (entry->Flags & CA_GotHeader) {
+ Cache_unref_data(entry);
+ }
}
- return;
} else if (Op == IOAbort) {
/* unused */
MSG("a_Cache_process_dbuf Op = IOAbort; not implemented!\n");
- return;
- }
-
- /*
- * Cache_get_header() will set CA_GotHeader if it has a full header, and
- * Cache_parse_header() will unset it if the header turns out to have been
- * merely an informational response from the server (i.e., 100 Continue)
- */
- while (!(entry->Flags & CA_GotHeader) &&
- (len = Cache_get_header(entry, buf + offset, buf_size - offset))) {
- offset += len;
- /* Let's scan, allocate, and set things according to header info */
- Cache_parse_header(entry);
}
-
- if (!(entry->Flags & CA_GotHeader))
- return;
-
- str = buf + offset;
- len = buf_size - offset;
- entry->TransferSize += len;
-
- if (entry->TransferDecoder) {
- Dstr *dbuf = a_Decode_process(entry->TransferDecoder, str, len);
- str = dbuf->str;
- len = dbuf->len;
- dStr_free(dbuf, 0);
- }
- if (entry->ContentDecoder) {
- Dstr *dbuf = a_Decode_process(entry->ContentDecoder, str, len);
- if (entry->TransferDecoder)
- dFree((char *)str);
- str = dbuf->str;
- len = dbuf->len;
- dStr_free(dbuf, 0);
- }
- dStr_append_l(entry->Data, str, len);
-
- if (entry->UTF8Data) {
- Dstr *dbuf = a_Decode_process(entry->CharsetDecoder, str, len);
- dStr_append_l(entry->UTF8Data, dbuf->str, dbuf->len);
- dStr_free(dbuf, 1);
- }
-
- if (entry->TransferDecoder || entry->ContentDecoder)
- dFree((char *)str);
-
- if (entry->Data->len)
- entry->Flags &= ~CA_IsEmpty;
-
- Cache_process_queue(entry);
}
/*
@@ -912,7 +941,7 @@ static int Cache_redirect(CacheEntry_t *entry, int Flags, BrowserWindow *bw)
NewUrl = a_Url_new(URL_STR_(entry->Location), URL_STR_(entry->Url));
if (entry->Flags & CA_TempRedirect)
a_Url_set_flags(NewUrl, URL_FLAGS(NewUrl) | URL_E2EQuery);
- a_Nav_push(bw, NewUrl);
+ a_Nav_push(bw, NewUrl, entry->Url);
a_Url_free(NewUrl);
} else {
/* Sub entity redirection (most probably an image) */
@@ -926,11 +955,55 @@ static int Cache_redirect(CacheEntry_t *entry, int Flags, BrowserWindow *bw)
return 0;
}
+typedef struct {
+ Dlist *auth;
+ DilloUrl *url;
+ BrowserWindow *bw;
+} CacheAuthData_t;
+
+/*
+ * Ask for user/password and reload the page.
+ */
+static void Cache_auth_callback(void *vdata)
+{
+ CacheAuthData_t *data = (CacheAuthData_t *)vdata;
+ if (a_Auth_do_auth(data->auth, data->url))
+ a_Nav_reload(data->bw);
+ Cache_auth_free(data->auth);
+ a_Url_free(data->url);
+ dFree(data);
+ Cache_auth_entry(NULL, NULL);
+ a_Timeout_remove();
+}
+
+/*
+ * Set a timeout function to ask for user/password.
+ */
+static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw)
+{
+ static int busy = 0;
+ CacheAuthData_t *data;
+
+ if (!entry) {
+ busy = 0;
+ } else if (busy) {
+ MSG_WARN("Cache_auth_entry: caught busy!\n");
+ } else if (entry->Auth) {
+ busy = 1;
+ data = dNew(CacheAuthData_t, 1);
+ data->auth = entry->Auth;
+ data->url = a_Url_dup(entry->Url);
+ data->bw = bw;
+ entry->Auth = NULL;
+ a_Timeout_add(0.0, Cache_auth_callback, data);
+ }
+}
+
/*
* Check whether a URL scheme is downloadable.
* Return: 1 enabled, 0 disabled.
*/
-static int Cache_download_enabled(const DilloUrl *url)
+int a_Cache_download_enabled(const DilloUrl *url)
{
if (!dStrcasecmp(URL_SCHEME(url), "http") ||
!dStrcasecmp(URL_SCHEME(url), "https") ||
@@ -961,6 +1034,24 @@ static void Cache_null_client(int Op, CacheClient_t *Client)
return;
}
+typedef struct {
+ BrowserWindow *bw;
+ DilloUrl *url;
+} Cache_savelink_t;
+
+/*
+ * Save link from behind a timeout so that Cache_process_queue() can
+ * get on with its work.
+ */
+static void Cache_savelink_cb(void *vdata)
+{
+ Cache_savelink_t *data = (Cache_savelink_t*) vdata;
+
+ a_UIcmd_save_link(data->bw, data->url);
+ a_Url_free(data->url);
+ dFree(data);
+}
+
/*
* Update cache clients for a single cache-entry
* Tasks:
@@ -969,9 +1060,11 @@ static void Cache_null_client(int Op, CacheClient_t *Client)
* - Remove clients when done
* - Call redirect handler
*
+ * Return: Cache entry, which may be NULL if it has been removed.
+ *
* TODO: Implement CA_Abort Op in client callback
*/
-static void Cache_process_queue(CacheEntry_t *entry)
+static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry)
{
uint_t i;
int st;
@@ -988,7 +1081,7 @@ static void Cache_process_queue(CacheEntry_t *entry)
if (Busy)
MSG_ERR("FATAL!: >>>> Cache_process_queue Caught busy!!! <<<<\n");
if (!(entry->Flags & CA_GotHeader))
- return;
+ return entry;
if (!(entry->Flags & CA_GotContentType)) {
st = a_Misc_get_content_type_from_data(
entry->Data->str, entry->Data->len, &Type);
@@ -1002,7 +1095,7 @@ static void Cache_process_queue(CacheEntry_t *entry)
entry->TypeDet = dStrdup(Type);
entry->Flags |= CA_GotContentType;
} else
- return; /* i.e., wait for more data */
+ return entry; /* i.e., wait for more data */
}
Busy = TRUE;
@@ -1020,6 +1113,7 @@ static void Cache_process_queue(CacheEntry_t *entry)
if (TypeMismatch) {
a_UIcmd_set_msg(Client_bw,"HTTP warning: Content-Type '%s' "
"doesn't match the real data.", entry->TypeHdr);
+ OfferDownload = TRUE;
}
if (entry->Flags & CA_Redirect) {
if (!Client->Callback) {
@@ -1046,13 +1140,13 @@ static void Cache_process_queue(CacheEntry_t *entry)
if (TypeMismatch) {
AbortEntry = TRUE;
} else {
- const char *content_type = Cache_current_content_type(entry);
- st = a_Web_dispatch_by_type(content_type, ClientWeb,
+ const char *curr_type = Cache_current_content_type(entry);
+ st = a_Web_dispatch_by_type(curr_type, ClientWeb,
&Client->Callback, &Client->CbData);
if (st == -1) {
/* MIME type is not viewable */
if (ClientWeb->flags & WEB_RootUrl) {
- MSG("Content-Type '%s' not viewable.\n", content_type);
+ MSG("Content-Type '%s' not viewable.\n", curr_type);
/* prepare a download offer... */
AbortEntry = OfferDownload = TRUE;
} else {
@@ -1081,8 +1175,10 @@ static void Cache_process_queue(CacheEntry_t *entry)
if ((Client->BufSize = data->len) > 0) {
Client->Buf = data->str;
(Client->Callback)(CA_Send, Client);
- if (ClientWeb->flags & WEB_RootUrl)
- a_UIcmd_set_page_prog(Client_bw, data->len, 1);
+ if (ClientWeb->flags & WEB_RootUrl) {
+ /* show size of page received */
+ a_UIcmd_set_page_prog(Client_bw, entry->Data->len, 1);
+ }
}
/* Remove client when done */
@@ -1107,11 +1203,20 @@ static void Cache_process_queue(CacheEntry_t *entry)
/* Abort the entry, remove it from cache, and maybe offer download. */
DilloUrl *url = a_Url_dup(entry->Url);
a_Capi_conn_abort_by_url(url);
- Cache_entry_remove(entry, NULL);
- if (OfferDownload && Cache_download_enabled(url)) {
- a_UIcmd_save_link(Client_bw, url);
+ entry = NULL;
+ if (OfferDownload) {
+ /* Remove entry when 'conn' is already done */
+ Cache_entry_remove(NULL, url);
+ if (a_Cache_download_enabled(url)) {
+ Cache_savelink_t *data = dNew(Cache_savelink_t, 1);
+ data->bw = Client_bw;
+ data->url = a_Url_dup(url);
+ a_Timeout_add(0.0, Cache_savelink_cb, data);
+ }
}
a_Url_free(url);
+ } else if (entry->Auth && (entry->Flags & CA_GotData)) {
+ Cache_auth_entry(entry, Client_bw);
}
/* Trigger cleanup when there are no cache clients */
@@ -1122,21 +1227,19 @@ static void Cache_process_queue(CacheEntry_t *entry)
Busy = FALSE;
_MSG("QueueSize ====> %d\n", dList_length(ClientQueue));
+ return entry;
}
/*
* Callback function for Cache_delayed_process_queue.
*/
-static void Cache_delayed_process_queue_callback(void *data)
+static void Cache_delayed_process_queue_callback()
{
CacheEntry_t *entry;
while ((entry = (CacheEntry_t *)dList_nth_data(DelayedQueue, 0))) {
Cache_ref_data(entry);
- Cache_process_queue(entry);
- if (entry != dList_nth_data(DelayedQueue, 0)) {
- /* Cache_process_queue() has removed the entry! */
- } else {
+ if ((entry = Cache_process_queue(entry))) {
Cache_unref_data(entry);
dList_remove(DelayedQueue, entry);
}
@@ -1189,10 +1292,23 @@ CacheClient_t *a_Cache_client_get_if_unique(int Key)
void a_Cache_stop_client(int Key)
{
CacheClient_t *Client;
+ CacheEntry_t *entry;
+ DICacheEntry *DicEntry;
+ /* The client can be in both queues at the same time */
if ((Client = dList_find_custom(ClientQueue, INT2VOIDP(Key),
Cache_client_by_key_cmp))) {
+ /* Dicache */
+ if ((DicEntry = a_Dicache_get_entry(Client->Url, Client->Version)))
+ a_Dicache_unref(Client->Url, Client->Version);
+
+ /* DelayedQueue */
+ if ((entry = Cache_entry_search(Client->Url)))
+ dList_remove(DelayedQueue, entry);
+
+ /* Main queue */
Cache_client_dequeue(Client, NULLKey);
+
} else {
_MSG("WARNING: Cache_stop_client, nonexistent client\n");
}
@@ -1213,7 +1329,7 @@ void a_Cache_freeall(void)
/* Remove every cache entry */
while ((data = dList_nth_data(CachedURLs, 0))) {
- dList_remove(CachedURLs, data);
+ dList_remove_fast(CachedURLs, data);
Cache_entry_free(data);
}
/* Remove the cache list */
diff --git a/src/cache.h b/src/cache.h
index 6099447c..c01bec55 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -46,6 +46,7 @@ typedef void (*CA_Callback_t)(int Op, CacheClient_t *Client);
struct _CacheClient {
int Key; /* Primary Key for this client */
const DilloUrl *Url; /* Pointer to a cache entry Url */
+ int Version; /* Dicache version of this Url (0 if not used) */
void *Buf; /* Pointer to cache-data */
uint_t BufSize; /* Valid size of cache-data */
CA_Callback_t Callback; /* Client function */
@@ -62,11 +63,12 @@ int a_Cache_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
void a_Cache_unref_buf(const DilloUrl *Url);
const char *a_Cache_get_content_type(const DilloUrl *url);
const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype,
- bool_t force);
+ const char *from);
uint_t a_Cache_get_flags(const DilloUrl *url);
+uint_t a_Cache_get_flags_with_redirection(const DilloUrl *url);
void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
const DilloUrl *Url);
-void a_Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds);
+int a_Cache_download_enabled(const DilloUrl *url);
void a_Cache_entry_remove_by_url(DilloUrl *url);
void a_Cache_freeall(void);
CacheClient_t *a_Cache_client_get_if_unique(int Key);
diff --git a/src/capi.c b/src/capi.c
index efa2cd93..3df48628 100644
--- a/src/capi.c
+++ b/src/capi.c
@@ -22,7 +22,6 @@
#include "IO/IO.h" /* for IORead &friends */
#include "IO/Url.h"
#include "chain.h"
-#include "list.h"
#include "history.h"
#include "nav.h"
#include "dpiapi.h"
@@ -56,10 +55,10 @@ enum {
* Local data
*/
/* Data list for active dpi connections */
-static capi_conn_t **DpiConn = NULL;
-static int DpiConnSize;
-static int DpiConnMax = 4;
-
+static Dlist *CapiConns; /* Data list for active connections; it holds
+ * pointers to capi_conn_t structures. */
+/* Last URL asked for view source */
+static DilloUrl *CapiVsUrl = NULL;
/*
* Forward declarations
@@ -75,7 +74,9 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
*/
void a_Capi_init(void)
{
- /* nothing to do for capi yet, just for cache */
+ /* create an empty list */
+ CapiConns = dList_new(32);
+ /* init cache */
a_Cache_init();
}
@@ -85,7 +86,7 @@ void a_Capi_init(void)
* Create a new connection data structure
*/
static capi_conn_t *
- Capi_conn_new(DilloUrl *url, void *bw, char *server, char *datastr)
+ Capi_conn_new(const DilloUrl *url, void *bw, char *server, char *datastr)
{
capi_conn_t *conn;
@@ -103,15 +104,22 @@ static capi_conn_t *
}
/*
+ * Validate a capi_conn_t pointer.
+ * Return value: NULL if not valid, conn otherwise.
+ */
+static capi_conn_t *Capi_conn_valid(capi_conn_t *conn)
+{
+ return dList_find(CapiConns, conn);
+}
+
+/*
* Increment the reference count and add to the list if not present
*/
static void Capi_conn_ref(capi_conn_t *conn)
{
if (++conn->Ref == 1) {
/* add the connection data to list */
- a_List_add(DpiConn, DpiConnSize, DpiConnMax);
- DpiConn[DpiConnSize] = conn;
- DpiConnSize++;
+ dList_append(CapiConns, (void *)conn);
}
_MSG(" Capi_conn_ref #%d %p\n", conn->Ref, conn);
}
@@ -121,26 +129,30 @@ static void Capi_conn_ref(capi_conn_t *conn)
*/
static void Capi_conn_unref(capi_conn_t *conn)
{
- int i, j;
-
_MSG(" Capi_conn_unref #%d %p\n", conn->Ref - 1, conn);
+ /* We may validate conn here, but it doesn't *seem* necessary */
if (--conn->Ref == 0) {
- for (i = 0; i < DpiConnSize; ++i) {
- if (DpiConn[i] == conn) {
- /* remove conn preserving the list order */
- for (j = i; j + 1 < DpiConnSize; ++j)
- DpiConn[j] = DpiConn[j + 1];
- --DpiConnSize;
- /* free dynamic memory */
- a_Url_free(conn->url);
- dFree(conn->server);
- dFree(conn->datastr);
- dFree(conn);
- break;
- }
- }
+ /* remove conn preserving the list order */
+ dList_remove(CapiConns, (void *)conn);
+ /* free dynamic memory */
+ a_Url_free(conn->url);
+ dFree(conn->server);
+ dFree(conn->datastr);
+ dFree(conn);
}
+ _MSG(" Capi_conn_unref CapiConns=%d\n", dList_length(CapiConns));
+}
+
+/*
+ * Compare function for searching a conn by server string
+ */
+static int Capi_conn_by_server_cmp(const void *v1, const void *v2)
+{
+ const capi_conn_t *node = v1;
+ const char *server = v2;
+ dReturn_val_if_fail(node && node->server && server, 1);
+ return strcmp(node->server, server);
}
/*
@@ -148,13 +160,7 @@ static void Capi_conn_unref(capi_conn_t *conn)
*/
static capi_conn_t *Capi_conn_find(char *server)
{
- int i;
-
- for (i = 0; i < DpiConnSize; ++i)
- if (strcmp(server, DpiConn[i]->server) == 0)
- return DpiConn[i];
-
- return NULL;
+ return dList_find_custom(CapiConns, (void*)server, Capi_conn_by_server_cmp);
}
/*
@@ -164,12 +170,15 @@ static void Capi_conn_resume(void)
{
int i;
DataBuf *dbuf;
+ capi_conn_t *conn;
- for (i = 0; i < DpiConnSize; ++i) {
- if (DpiConn[i]->Flags & PENDING) {
- capi_conn_t *conn = DpiConn[i];
+ for (i = 0; i < dList_length(CapiConns); ++i) {
+ conn = dList_nth_data (CapiConns, i);
+ if (conn->Flags & PENDING) {
dbuf = a_Chain_dbuf_new(conn->datastr,(int)strlen(conn->datastr), 0);
- a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
+ if (conn->InfoSend) {
+ a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
+ }
dFree(dbuf);
conn->Flags &= ~PENDING;
}
@@ -178,14 +187,18 @@ static void Capi_conn_resume(void)
/*
* Abort the connection for a given url, using its CCC.
+ * (OpAbort 2,BCK removes the cache entry)
+ * TODO: when conn is already done, the cache entry isn't removed.
+ * This may be wrong and needs a revision.
*/
void a_Capi_conn_abort_by_url(const DilloUrl *url)
{
int i;
+ capi_conn_t *conn;
- for (i = 0; i < DpiConnSize; ++i) {
- if (a_Url_cmp(DpiConn[i]->url, url) == 0) {
- capi_conn_t *conn = DpiConn[i];
+ for (i = 0; i < dList_length(CapiConns); ++i) {
+ conn = dList_nth_data (CapiConns, i);
+ if (a_Url_cmp(conn->url, url) == 0) {
if (conn->InfoSend) {
a_Capi_ccc(OpAbort, 1, BCK, conn->InfoSend, NULL, NULL);
}
@@ -200,21 +213,35 @@ void a_Capi_conn_abort_by_url(const DilloUrl *url)
/* ------------------------------------------------------------------------- */
/*
- * Safety test: only allow dpi-urls from dpi-generated pages.
+ * Store the last URL requested by "view source"
*/
-static int Capi_dpi_verify_request(DilloWeb *web)
+void a_Capi_set_vsource_url(const DilloUrl *url)
{
- DilloUrl *referer;
+ a_Url_free(CapiVsUrl);
+ CapiVsUrl = a_Url_dup(url);
+}
+
+/*
+ * Safety test: only allow GET|POST dpi-urls from dpi-generated pages.
+ */
+int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url)
+{
+ const DilloUrl *referer;
int allow = FALSE;
- /* test POST and GET */
- if (dStrcasecmp(URL_SCHEME(web->url), "dpi") == 0 &&
- URL_FLAGS(web->url) & (URL_Post + URL_Get)) {
- /* only allow dpi requests from dpi-generated urls */
- if (a_Nav_stack_size(web->bw)) {
- referer = a_History_get_url(NAV_TOP_UIDX(web->bw));
- if (dStrcasecmp(URL_SCHEME(referer), "dpi") == 0) {
- allow = TRUE;
+ if (dStrcasecmp(URL_SCHEME(url), "dpi") == 0) {
+ if (!(URL_FLAGS(url) & (URL_Post + URL_Get))) {
+ allow = TRUE;
+ } else if (!(URL_FLAGS(url) & URL_Post) &&
+ strncmp(URL_STR(url), "dpi:/vsource/", 13) == 0) {
+ allow = TRUE;
+ } else {
+ /* only allow GET&POST dpi-requests from dpi-generated urls */
+ if (a_Nav_stack_size(bw)) {
+ referer = a_History_get_url(NAV_TOP_UIDX(bw));
+ if (dStrcasecmp(URL_SCHEME(referer), "dpi") == 0) {
+ allow = TRUE;
+ }
}
}
} else {
@@ -222,10 +249,10 @@ static int Capi_dpi_verify_request(DilloWeb *web)
}
if (!allow) {
- MSG("Capi_dpi_verify_request: Permission Denied!\n");
- MSG(" URL_STR : %s\n", URL_STR(web->url));
- if (URL_FLAGS(web->url) & URL_Post) {
- MSG(" URL_DATA: %s\n", dStr_printable(URL_DATA(web->url), 1024));
+ MSG("a_Capi_dpi_verify_request: Permission Denied!\n");
+ MSG(" URL_STR : %s\n", URL_STR(url));
+ if (URL_FLAGS(url) & URL_Post) {
+ MSG(" URL_DATA: %s\n", dStr_printable(URL_DATA(url), 1024));
}
}
return allow;
@@ -265,7 +292,6 @@ static int Capi_url_uses_dpi(DilloUrl *url, char **server_ptr)
/*
* Build the dpip command tag, according to URL and server.
- * TODO: make it PROXY-aware (AFAIS, it should be easy)
*/
static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
{
@@ -273,10 +299,20 @@ static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
if (strcmp(server, "proto.https") == 0) {
/* Let's be kind and make the HTTP query string for the dpi */
+ char *proxy_connect = a_Http_make_connect_str(web->url);
Dstr *http_query = a_Http_make_query_str(web->url, FALSE);
/* BUG: embedded NULLs in query data will truncate message */
- cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s",
- "open_url", URL_STR(web->url), http_query->str);
+ if (proxy_connect) {
+ const char *proxy_urlstr = a_Http_get_proxy_urlstr();
+ cmd = a_Dpip_build_cmd("cmd=%s proxy_url=%s proxy_connect=%s "
+ "url=%s query=%s", "open_url", proxy_urlstr,
+ proxy_connect, URL_STR(web->url),
+ http_query->str);
+ } else {
+ cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s",
+ "open_url", URL_STR(web->url),http_query->str);
+ }
+ dFree(proxy_connect);
dStr_free(http_query, 1);
} else if (strcmp(server, "downloads") == 0) {
@@ -292,6 +328,84 @@ static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
}
/*
+ * Send the requested URL's source to the "view source" dpi
+ */
+static void Capi_dpi_send_source(BrowserWindow *bw, DilloUrl *url)
+{
+ char *p, *buf, *cmd, size_str[32], *server="vsource";
+ int buf_size;
+
+ if (!(p = strchr(URL_STR(url), ':')) || !(p = strchr(p + 1, ':')))
+ return;
+
+ if (a_Capi_get_buf(CapiVsUrl, &buf, &buf_size)) {
+ /* send the page's source to this dpi connection */
+ snprintf(size_str, 32, "%d", buf_size);
+ cmd = a_Dpip_build_cmd("cmd=%s url=%s data_size=%s",
+ "start_send_page", URL_STR(url), size_str);
+ a_Capi_dpi_send_cmd(NULL, bw, cmd, server, 0);
+ a_Capi_dpi_send_data(url, bw, buf, buf_size, server, 0);
+ } else {
+ cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
+ "DpiError", "Page is NOT cached");
+ a_Capi_dpi_send_cmd(NULL, bw, cmd, server, 0);
+ }
+ dFree(cmd);
+}
+
+/*
+ * When dillo wants to open an URL, this can be either due to user action
+ * (e.g., typing in an URL, clicking a link), or automatic (HTTP header
+ * indicates redirection, META HTML tag with refresh attribute and 0 delay,
+ * and images and stylesheets on an HTML page when autoloading is enabled).
+ *
+ * For a user request, the action will be permitted.
+ * For an automatic request, permission to load depends on the filter set
+ * by the user.
+ */
+static bool_t Capi_filters_allow(const DilloUrl *wanted,
+ const DilloUrl *requester)
+{
+ bool_t ret;
+
+ if (requester == NULL) {
+ /* request made by user */
+ ret = TRUE;
+ } else {
+ switch (prefs.filter_auto_requests) {
+ case PREFS_FILTER_SAME_DOMAIN:
+ {
+ const char *req_host = URL_HOST(requester),
+ *want_host = URL_HOST(wanted),
+ *req_suffix,
+ *want_suffix;
+ if (want_host[0] == '\0') {
+ ret = (req_host[0] == '\0' ||
+ !dStrcasecmp(URL_SCHEME(wanted), "data")) ? TRUE : FALSE;
+ } else {
+ /* This will regard "www.dillo.org" and "www.dillo.org." as
+ * different, but it doesn't seem worth caring about.
+ */
+ req_suffix = a_Url_host_find_public_suffix(req_host);
+ want_suffix = a_Url_host_find_public_suffix(want_host);
+
+ ret = dStrcasecmp(req_suffix, want_suffix) == 0;
+ }
+
+ MSG("Capi_filters_allow: from %s to %s: %s\n", req_host, want_host,
+ ret ? "ALLOWED" : "DENIED");
+ break;
+ }
+ case PREFS_FILTER_ALLOW_ALL:
+ default:
+ ret = TRUE;
+ break;
+ }
+ }
+ return ret;
+}
+
+/*
* Most used function for requesting a URL.
* TODO: clean up the ad-hoc bindings with an API that allows dynamic
* addition of new plugins.
@@ -301,12 +415,15 @@ static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
*/
int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
{
- capi_conn_t *conn;
int reload;
char *cmd, *server;
+ capi_conn_t *conn = NULL;
const char *scheme = URL_SCHEME(web->url);
int safe = 0, ret = 0, use_cache = 0;
+ dReturn_val_if_fail((a_Capi_get_flags(web->url) & CAPI_IsCached) ||
+ Capi_filters_allow(web->url, web->requester), 0);
+
/* reload test */
reload = (!(a_Capi_get_flags(web->url) & CAPI_IsCached) ||
(URL_FLAGS(web->url) & URL_E2EQuery));
@@ -314,35 +431,43 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
if (web->flags & WEB_Download) {
/* download request: if cached save from cache, else
* for http, ftp or https, use the downloads dpi */
- if (a_Capi_get_flags(web->url) & CAPI_IsCached) {
- if (web->filename && (web->stream = fopen(web->filename, "w"))) {
- use_cache = 1;
- }
- } else {
- if (!dStrcasecmp(scheme, "https") ||
- !dStrcasecmp(scheme, "http") ||
- !dStrcasecmp(scheme, "ftp")) {
- server = "downloads";
- cmd = Capi_dpi_build_cmd(web, server);
- a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
- dFree(cmd);
+ if (a_Capi_get_flags_with_redirection(web->url) & CAPI_IsCached) {
+ if (web->filename) {
+ if ((web->stream = fopen(web->filename, "w"))) {
+ use_cache = 1;
+ } else {
+ MSG_WARN("Cannot open \"%s\" for writing.\n", web->filename);
+ }
}
+ } else if (a_Cache_download_enabled(web->url)) {
+ server = "downloads";
+ cmd = Capi_dpi_build_cmd(web, server);
+ a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
+ dFree(cmd);
}
} else if (Capi_url_uses_dpi(web->url, &server)) {
/* dpi request */
- if ((safe = Capi_dpi_verify_request(web))) {
+ if ((safe = a_Capi_dpi_verify_request(web->bw, web->url))) {
if (dStrcasecmp(scheme, "dpi") == 0) {
- /* make "dpi:/" prefixed urls always reload. */
- a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EQuery);
- reload = 1;
+ if (strcmp(server, "vsource") == 0) {
+ /* don't reload the "view source" page */
+ } else {
+ /* make the other "dpi:/" prefixed urls always reload. */
+ a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EQuery);
+ reload = 1;
+ }
}
if (reload) {
a_Capi_conn_abort_by_url(web->url);
/* Send dpip command */
+ _MSG("a_Capi_open_url, reload url='%s'\n", URL_STR(web->url));
cmd = Capi_dpi_build_cmd(web, server);
a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
dFree(cmd);
+ if (strcmp(server, "vsource") == 0) {
+ Capi_dpi_send_source(web->bw, web->url);
+ }
}
use_cache = 1;
}
@@ -354,6 +479,9 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
a_Capi_conn_abort_by_url(web->url);
/* create a new connection and start the CCC operations */
conn = Capi_conn_new(web->url, web->bw, "http", "none");
+ /* start the reception branch before the query one because the DNS
+ * may callback immediatly. This may avoid a race condition. */
+ a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, "http");
a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, web);
}
use_cache = 1;
@@ -364,7 +492,10 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
}
if (use_cache) {
- ret = a_Cache_open_url(web, Call, CbData);
+ if (!conn || (conn && Capi_conn_valid(conn))) {
+ /* not aborted, let's continue... */
+ ret = a_Cache_open_url(web, Call, CbData);
+ }
} else {
a_Web_free(web);
}
@@ -372,12 +503,11 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
}
/*
- * Return status information of an URL's content-transfer process.
+ * Convert cache-defined flags to Capi ones.
*/
-int a_Capi_get_flags(const DilloUrl *Url)
+static int Capi_map_cache_flags(uint_t flags)
{
int status = 0;
- uint_t flags = a_Cache_get_flags(Url);
if (flags) {
status |= CAPI_IsCached;
@@ -394,6 +524,26 @@ int a_Capi_get_flags(const DilloUrl *Url)
}
/*
+ * Return status information of an URL's content-transfer process.
+ */
+int a_Capi_get_flags(const DilloUrl *Url)
+{
+ uint_t flags = a_Cache_get_flags(Url);
+ int status = flags ? Capi_map_cache_flags(flags) : 0;
+ return status;
+}
+
+/*
+ * Same as a_Capi_get_flags() but following redirections.
+ */
+int a_Capi_get_flags_with_redirection(const DilloUrl *Url)
+{
+ uint_t flags = a_Cache_get_flags_with_redirection(Url);
+ int status = flags ? Capi_map_cache_flags(flags) : 0;
+ return status;
+}
+
+/*
* Get the cache's buffer for the URL, and its size.
* Return: 1 cached, 0 not cached.
*/
@@ -419,20 +569,21 @@ const char *a_Capi_get_content_type(const DilloUrl *url)
}
/*
- * Set the Content-Type for the URL.
+ * Set the Content-Type for the URL.
*/
const char *a_Capi_set_content_type(const DilloUrl *url, const char *ctype,
- bool_t force)
+ const char *from)
{
- return a_Cache_set_content_type(url, ctype, force);
+ return a_Cache_set_content_type(url, ctype, from);
}
/*
- * Send a dpi cmd.
- * (For instance: add_bookmark, open_url, send_preferences, ...)
+ * Send data to a dpi (e.g. add_bookmark, open_url, send_preferences, ...)
+ * Most of the time we send dpi commands, but it also serves for raw data
+ * as with "view source".
*/
-int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
- int flags)
+int a_Capi_dpi_send_data(const DilloUrl *url, void *bw,
+ char *data, int data_sz, char *server, int flags)
{
capi_conn_t *conn;
DataBuf *dbuf;
@@ -441,8 +592,9 @@ int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
/* open a new connection to server */
/* Create a new connection data struct and add it to the list */
- conn = Capi_conn_new(url, bw, server, cmd);
+ conn = Capi_conn_new(url, bw, server, data);
/* start the CCC operations */
+ a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, server);
a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, server);
} else {
@@ -450,11 +602,11 @@ int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
conn = Capi_conn_find(server);
if (conn) {
/* found */
- dbuf = a_Chain_dbuf_new(cmd, (int)strlen(cmd), 0);
+ dbuf = a_Chain_dbuf_new(data, data_sz, 0);
a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
dFree(dbuf);
} else {
- MSG(" ERROR: [a_Capi_dpi_send_cmd] No open connection found\n");
+ MSG(" ERROR: [a_Capi_dpi_send_data] No open connection found\n");
}
}
@@ -462,6 +614,16 @@ int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
}
/*
+ * Send a dpi cmd.
+ * (For instance: add_bookmark, open_url, send_preferences, ...)
+ */
+int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
+ int flags)
+{
+ return a_Capi_dpi_send_data(url, bw, cmd, strlen(cmd), server, flags);
+}
+
+/*
* Remove a client from the cache client queue.
* force = also abort the CCC if this is the last client.
*/
@@ -469,7 +631,11 @@ void a_Capi_stop_client(int Key, int force)
{
CacheClient_t *Client;
- if (force && (Client = a_Cache_client_get_if_unique(Key))) {
+ _MSG("a_Capi_stop_client: force=%d\n", force);
+
+ Client = a_Cache_client_get_if_unique(Key);
+ if (Client && (force || Client->BufSize == 0)) {
+ /* remove empty entries too */
a_Capi_conn_abort_by_url(Client->Url);
}
a_Cache_stop_client(Key);
@@ -525,18 +691,17 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* FWD */
+ } else { /* 1 FWD */
/* Command sending branch (status) */
switch (Op) {
case OpSend:
if (!Data2) {
MSG_WARN("Capi.c: Opsend [1F] Data2 = NULL\n");
- } else if (strcmp(Data2, "SockFD") == 0) {
- /* start the receiving branch */
- capi_conn_t *conn = Info->LocalKey;
+ } else if (strcmp(Data2, "FD") == 0) {
+ conn = Info->LocalKey;
conn->SockFD = *(int*)Data1;
- a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), Info->LocalKey,
- conn->server);
+ /* communicate the FD through the answer branch */
+ a_Capi_ccc(OpSend, 2, BCK, conn->InfoRecv, &conn->SockFD, "FD");
} else if (strcmp(Data2, "DpidOK") == 0) {
/* resume pending dpi requests */
Capi_conn_resume();
@@ -545,13 +710,19 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
case OpAbort:
conn = Info->LocalKey;
conn->InfoSend = NULL;
- /* remove the cache entry for this URL */
- a_Cache_entry_remove_by_url(conn->url);
- if (Data2 && !strcmp(Data2, "DpidERROR"))
- a_UIcmd_set_msg(conn->bw,
- "ERROR: can't start dpid daemon "
- "(URL scheme = '%s')!",
- URL_SCHEME(conn->url));
+ if (Data2) {
+ if (!strcmp(Data2, "DpidERROR")) {
+ a_UIcmd_set_msg(conn->bw,
+ "ERROR: can't start dpid daemon "
+ "(URL scheme = '%s')!",
+ conn->url ? URL_SCHEME(conn->url) : "");
+ } else if (!strcmp(Data2, "Both") && conn->InfoRecv) {
+ /* abort the other branch too */
+ a_Capi_ccc(OpAbort, 2, BCK, conn->InfoRecv, NULL, NULL);
+ }
+ }
+ /* if URL == expect-url */
+ a_Nav_cancel_expect_if_eq(conn->bw, conn->url);
/* finish conn */
Capi_conn_unref(conn);
dFree(Info);
@@ -564,21 +735,29 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
} else if (Branch == 2) {
if (Dir == BCK) {
- /* Server listening branch (status)
- * (Data1 = conn; Data2 = {"HttpFD" | "DpiFD"}) */
+ /* Answer branch */
switch (Op) {
case OpStart:
+ /* Data1 = conn; Data2 = {"http" | "<dpi server name>"} */
conn = Data1;
Capi_conn_ref(conn);
Info->LocalKey = conn;
conn->InfoRecv = Info;
a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 2);
- a_Chain_bcb(OpStart, Info, &conn->SockFD, Data2);
+ a_Chain_bcb(OpStart, Info, NULL, Data2);
+ break;
+ case OpSend:
+ /* Data1 = FD */
+ if (Data2 && strcmp(Data2, "FD") == 0) {
+ a_Chain_bcb(OpSend, Info, Data1, Data2);
+ }
break;
case OpAbort:
conn = Info->LocalKey;
conn->InfoRecv = NULL;
a_Chain_bcb(OpAbort, Info, NULL, NULL);
+ /* remove the cache entry for this URL */
+ a_Cache_entry_remove_by_url(conn->url);
Capi_conn_unref(conn);
dFree(Info);
break;
@@ -586,7 +765,7 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* FWD */
+ } else { /* 2 FWD */
/* Server listening branch */
switch (Op) {
case OpSend:
diff --git a/src/capi.h b/src/capi.h
index c30dc6cf..8a0c7095 100644
--- a/src/capi.h
+++ b/src/capi.h
@@ -27,10 +27,15 @@ int a_Capi_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
void a_Capi_unref_buf(const DilloUrl *Url);
const char *a_Capi_get_content_type(const DilloUrl *url);
const char *a_Capi_set_content_type(const DilloUrl *url, const char *ctype,
- bool_t force);
+ const char *from);
int a_Capi_get_flags(const DilloUrl *Url);
+int a_Capi_get_flags_with_redirection(const DilloUrl *Url);
+int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url);
+int a_Capi_dpi_send_data(const DilloUrl *url, void *bw,
+ char *data, int data_sz, char *server, int flags);
int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
int flags);
+void a_Capi_set_vsource_url(const DilloUrl *url);
void a_Capi_stop_client(int Key, int force);
void a_Capi_conn_abort_by_url(const DilloUrl *url);
diff --git a/src/chain.c b/src/chain.c
index 37f43a7f..d4098a2d 100644
--- a/src/chain.c
+++ b/src/chain.c
@@ -104,6 +104,7 @@ int a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2)
if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
/* CCC is not operative */
} else if (Info->Fcb) {
+ /* flag the caller */
if (Op == OpEnd)
Info->Flags |= CCC_Ended;
else if (Op == OpAbort)
@@ -126,6 +127,7 @@ int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2)
if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
/* CCC is not operative */
} else if (Info->Bcb) {
+ /* flag the caller */
if (Op == OpEnd)
Info->Flags |= CCC_Ended;
else if (Op == OpAbort)
@@ -137,6 +139,28 @@ int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2)
return ret;
}
+/*
+ * Issue the backward callback of the 'Info' link and then the
+ * forward callback (used for OpAbort and OpStop).
+ * Return value: 1 if OK, 0 if not operative.
+ */
+int a_Chain_bfcb(int Op, ChainLink *Info, void *Data1, void *Data2)
+{
+ int ret;
+
+ ret = a_Chain_bcb(Op, Info, Data1, Data2);
+ if (ret == 1) {
+ /* we need to clear the flag to reuse this 'Info' ChainLink */
+ if (Op == OpEnd)
+ Info->Flags &= ~CCC_Ended;
+ else if (Op == OpAbort)
+ Info->Flags &= ~CCC_Aborted;
+
+ ret = a_Chain_fcb(Op, Info, Data1, Data2);
+ }
+ return ret;
+}
+
/*
* Allocate and initialize a new DataBuf structure
@@ -153,7 +177,7 @@ DataBuf *a_Chain_dbuf_new(void *buf, int size, int code)
/*
* Check whether the CCC is operative.
* Also used to hook debug information.
- *
+ *
* Return value: 1 if ready to use, 0 if not operative.
*/
int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir,
@@ -166,7 +190,9 @@ int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir,
if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
/* CCC is not operative */
- MSG_WARN("CCC: call on already finished chain.\n");
+ MSG_WARN("CCC: call on already finished chain. Flags=%s%s\n",
+ Info->Flags & CCC_Ended ? "CCC_Ended " : "",
+ Info->Flags & CCC_Aborted ? "CCC_Aborted" : "");
} else {
ret = 1;
}
diff --git a/src/chain.h b/src/chain.h
index b6b41bd4..fd86557c 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -69,6 +69,7 @@ ChainLink *a_Chain_link_new(ChainLink *AInfo, ChainFunction_t AFunc,
void a_Chain_unlink(ChainLink *Info, int Direction);
int a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2);
int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2);
+int a_Chain_bfcb(int Op, ChainLink *Info, void *Data1, void *Data2);
int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir,
ChainLink *Info);
diff --git a/src/colors.c b/src/colors.c
index 9a4c8c8e..5b647bb2 100644
--- a/src/colors.c
+++ b/src/colors.c
@@ -204,7 +204,7 @@ static const struct key {
#define NCOLORS (sizeof(color_keyword) / sizeof(struct key))
/*
- * Parse a color in hex (RRGGBB)
+ * Parse a color in hex (RRGGBB) or (RGB)
*
* Return Value:
* parsed color if successful (err = 0),
@@ -219,7 +219,12 @@ static int32_t Color_parse_hex (const char *s, int32_t default_color, int *err)
ret_color = strtol(s, &tail, 16);
if (tail - s == 6)
*err = 0;
- else
+ else if (tail - s == 3) { /* #RGB as allowed by CSS */
+ *err = 0;
+ ret_color = ((ret_color & 0xf00) << 12) | ((ret_color & 0xf00) << 8) |
+ ((ret_color & 0x0f0) << 8) | ((ret_color & 0x0f0) << 4) |
+ ((ret_color & 0x00f) << 4) | ((ret_color & 0x00f) << 0);
+ } else
ret_color = default_color;
return ret_color;
@@ -241,7 +246,7 @@ int32_t a_Color_parse (const char *subtag, int32_t default_color, int *err)
int ret, low, mid, high, st = 1;
/* skip leading spaces */
- for (cp = subtag; isspace(*cp); cp++);
+ for (cp = subtag; dIsspace(*cp); cp++);
ret_color = default_color;
if (*cp == '#') {
diff --git a/src/cookies.c b/src/cookies.c
index 1b336b83..7b9062e2 100644
--- a/src/cookies.c
+++ b/src/cookies.c
@@ -10,10 +10,9 @@
* (at your option) any later version.
*/
-/* Handling of cookies takes place here.
- * This implementation aims to follow RFC 2965:
- * http://www.ietf.org/rfc/rfc2965.txt
- */
+/* Handling of cookies takes place here. */
+
+#include "msg.h"
#ifdef DISABLE_COOKIES
@@ -34,15 +33,13 @@ void a_Cookies_init(void)
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
-#include <time.h> /* for time() and time_t */
#include <ctype.h>
+#include <errno.h>
-#include "msg.h"
#include "IO/Url.h"
#include "list.h"
#include "cookies.h"
#include "capi.h"
-#include "dpiapi.h"
#include "../dpip/dpip.h"
@@ -80,14 +77,19 @@ static int Cookie_control_init(void);
static FILE *Cookies_fopen(const char *filename, char *init_str)
{
FILE *F_in;
- int fd;
+ int fd, rc;
if ((F_in = fopen(filename, "r")) == NULL) {
/* Create the file */
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd != -1) {
- if (init_str)
- write(fd, init_str, strlen(init_str));
+ if (init_str) {
+ rc = write(fd, init_str, strlen(init_str));
+ if (rc == -1) {
+ MSG("Cookies: Could not write initial string to file %s: %s\n",
+ filename, dStrerror(errno));
+ }
+ }
close(fd);
MSG("Cookies: Created file: %s\n", filename);
@@ -134,10 +136,11 @@ void a_Cookies_freeall()
/*
* Set the value corresponding to the cookie string
*/
-void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url)
+void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url,
+ const char *date)
{
CookieControlAction action;
- char *cmd, *cookie_string, *dpip_tag, numstr[16];
+ char *cmd, *cookie_string, *dpip_tag;
const char *path;
int i;
@@ -152,10 +155,14 @@ void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url)
for (i = 0; (cookie_string = dList_nth_data(cookie_strings, i)); ++i) {
path = URL_PATH_(set_url);
- snprintf(numstr, 16, "%d", URL_PORT(set_url));
- cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s port=%s",
- "set_cookie", cookie_string, URL_HOST_(set_url),
- path ? path : "/", numstr);
+ if (date)
+ cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s date=%s",
+ "set_cookie", cookie_string,
+ URL_HOST_(set_url), path ? path : "/", date);
+ else
+ cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s",
+ "set_cookie", cookie_string,
+ URL_HOST_(set_url), path ? path : "/");
_MSG("Cookies.c: a_Cookies_set \n\t \"%s\" \n",cmd );
/* This call is commented because it doesn't guarantee the order
@@ -174,7 +181,7 @@ void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url)
*/
char *a_Cookies_get_query(const DilloUrl *request_url)
{
- char *cmd, *dpip_tag, *query, numstr[16];
+ char *cmd, *dpip_tag, *query;
const char *path;
CookieControlAction action;
@@ -188,10 +195,9 @@ char *a_Cookies_get_query(const DilloUrl *request_url)
}
path = URL_PATH_(request_url);
- snprintf(numstr, 16, "%d", URL_PORT(request_url));
- cmd = a_Dpip_build_cmd("cmd=%s scheme=%s host=%s path=%s port=%s",
+ cmd = a_Dpip_build_cmd("cmd=%s scheme=%s host=%s path=%s",
"get_cookie", URL_SCHEME(request_url),
- URL_HOST(request_url), path ? path : "/", numstr);
+ URL_HOST(request_url), path ? path : "/");
/* Get the answer from cookies.dpi */
_MSG("cookies.c: a_Dpi_send_blocking_cmd cmd = {%s}\n", cmd);
@@ -199,13 +205,11 @@ char *a_Cookies_get_query(const DilloUrl *request_url)
_MSG("cookies.c: after a_Dpi_send_blocking_cmd resp={%s}\n", dpip_tag);
dFree(cmd);
- query = dStrdup("Cookie2: $Version=\"1\"\r\n");
-
if (dpip_tag != NULL) {
- char *cookie = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cookie");
+ query = a_Dpip_get_attr(dpip_tag, "cookie");
dFree(dpip_tag);
- query = dStrconcat(query, cookie, NULL);
- dFree(cookie);
+ } else {
+ query = dStrdup("");
}
return query;
}
@@ -226,11 +230,10 @@ static int Cookie_control_init(void)
{
CookieControl cc;
FILE *stream;
- char *filename;
+ char *filename, *rc;
char line[LINE_MAXLEN];
char domain[LINE_MAXLEN];
char rule[LINE_MAXLEN];
- int i, j;
bool_t enabled = FALSE;
/* Get a file pointer */
@@ -244,28 +247,31 @@ static int Cookie_control_init(void)
/* Get all lines in the file */
while (!feof(stream)) {
line[0] = '\0';
- fgets(line, LINE_MAXLEN, stream);
+ rc = fgets(line, LINE_MAXLEN, stream);
+ if (!rc && ferror(stream)) {
+ MSG("Cookies1: Error while reading rule from cookiesrc: %s\n",
+ dStrerror(errno));
+ return 2; /* bail out */
+ }
/* Remove leading and trailing whitespaces */
dStrstrip(line);
if (line[0] != '\0' && line[0] != '#') {
- i = 0;
- j = 0;
+ int i = 0, j = 0;
/* Get the domain */
- while (!isspace(line[i]))
+ while (line[i] != '\0' && !dIsspace(line[i]))
domain[j++] = line[i++];
domain[j] = '\0';
/* Skip past whitespaces */
- i++;
- while (isspace(line[i]))
+ while (dIsspace(line[i]))
i++;
/* Get the rule */
j = 0;
- while (line[i] != '\0' && !isspace(line[i]))
+ while (line[i] != '\0' && !dIsspace(line[i]))
rule[j++] = line[i++];
rule[j] = '\0';
@@ -287,8 +293,17 @@ static int Cookie_control_init(void)
default_action = cc.action;
dFree(cc.domain);
} else {
+ int i;
+ uint_t len = strlen(cc.domain);
+
+ /* Insert into list such that longest rules come first. */
a_List_add(ccontrol, num_ccontrol, num_ccontrol_max);
- ccontrol[num_ccontrol++] = cc;
+ for (i = num_ccontrol++;
+ i > 0 && (len > strlen(ccontrol[i-1].domain));
+ i--) {
+ ccontrol[i] = ccontrol[i-1];
+ }
+ ccontrol[i] = cc;
}
if (cc.action != COOKIE_DENY)
@@ -302,7 +317,9 @@ static int Cookie_control_init(void)
}
/*
- * Check the rules for an appropriate action for this domain
+ * Check the rules for an appropriate action for this domain.
+ * The rules are ordered by domain length, with longest first, so the
+ * first match is the most specific.
*/
static CookieControlAction Cookies_control_check_domain(const char *domain)
{
diff --git a/src/cookies.h b/src/cookies.h
index 6f9f77e0..482aa5ae 100644
--- a/src/cookies.h
+++ b/src/cookies.h
@@ -12,7 +12,8 @@ extern "C" {
# define a_Cookies_freeall() ;
#else
char *a_Cookies_get_query(const DilloUrl *request_url);
- void a_Cookies_set(Dlist *cookie_string, const DilloUrl *set_url);
+ void a_Cookies_set(Dlist *cookie_string, const DilloUrl *set_url,
+ const char *server_date);
void a_Cookies_init( void );
void a_Cookies_freeall( void );
#endif
diff --git a/src/css.cc b/src/css.cc
new file mode 100644
index 00000000..988d0dc6
--- /dev/null
+++ b/src/css.cc
@@ -0,0 +1,617 @@
+/*
+ * File: css.cc
+ *
+ * Copyright 2008-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include "../dlib/dlib.h"
+#include "misc.h"
+#include "msg.h"
+#include "html_common.hh"
+#include "css.hh"
+#include "cssparser.hh"
+
+using namespace dw::core::style;
+
+void CssProperty::print () {
+ fprintf (stderr, "%s - %d\n",
+ CssParser::propertyNameString((CssPropertyName)name),
+ (int)value.intVal);
+}
+
+CssPropertyList::~CssPropertyList () {
+ if (ownerOfStrings)
+ for (int i = 0; i < size (); i++)
+ getRef (i)->free ();
+}
+
+/**
+ * \brief Set property to a given name and type.
+ */
+void CssPropertyList::set (CssPropertyName name, CssValueType type,
+ CssPropertyValue value) {
+ CssProperty *prop;
+
+ for (int i = 0; i < size (); i++) {
+ prop = getRef (i);
+
+ if (prop->name == name) {
+ if (ownerOfStrings)
+ prop->free ();
+ prop->type = type;
+ prop->value = value;
+ return;
+ }
+ }
+
+ increase ();
+ prop = getRef (size () - 1);
+ prop->name = name;
+ prop->type = type;
+ prop->value = value;
+}
+
+/**
+ * \brief Merge properties into argument property list.
+ */
+void CssPropertyList::apply (CssPropertyList *props) {
+ for (int i = 0; i < size (); i++)
+ props->set ((CssPropertyName) getRef (i)->name,
+ (CssValueType) getRef (i)->type,
+ getRef (i)->value);
+}
+
+void CssPropertyList::print () {
+ for (int i = 0; i < size (); i++)
+ getRef (i)->print ();
+}
+
+CssSelector::CssSelector () {
+ struct CombinatorAndSelector *cs;
+
+ refCount = 0;
+ selectorList = new lout::misc::SimpleVector
+ <struct CombinatorAndSelector> (1);
+ selectorList->increase ();
+ cs = selectorList->getRef (selectorList->size () - 1);
+
+ cs->notMatchingBefore = -1;
+ cs->selector = new CssSimpleSelector ();
+};
+
+CssSelector::~CssSelector () {
+ for (int i = selectorList->size () - 1; i >= 0; i--)
+ delete selectorList->getRef (i)->selector;
+ delete selectorList;
+}
+
+/**
+ * \brief Return whether selector matches at a given node in the document tree.
+ */
+bool CssSelector::match (Doctree *docTree, const DoctreeNode *node) {
+ CssSimpleSelector *sel;
+ Combinator comb = CHILD;
+ int *notMatchingBefore;
+ const DoctreeNode *n;
+
+ for (int i = selectorList->size () - 1; i >= 0; i--) {
+ struct CombinatorAndSelector *cs = selectorList->getRef (i);
+
+ sel = cs->selector;
+ notMatchingBefore = &cs->notMatchingBefore;
+
+ if (node == NULL)
+ return false;
+
+ switch (comb) {
+ case CHILD:
+ if (!sel->match (node))
+ return false;
+ break;
+ case DESCENDANT:
+ n = node;
+
+ while (true) {
+ if (node == NULL || node->num <= *notMatchingBefore) {
+ *notMatchingBefore = n->num;
+ return false;
+ }
+
+ if (sel->match (node))
+ break;
+
+ node = docTree->parent (node);
+ }
+ break;
+ default:
+ return false; // \todo implement other combinators
+ }
+
+ comb = cs->combinator;
+ node = docTree->parent (node);
+ }
+
+ return true;
+}
+
+void CssSelector::addSimpleSelector (Combinator c) {
+ struct CombinatorAndSelector *cs;
+
+ selectorList->increase ();
+ cs = selectorList->getRef (selectorList->size () - 1);
+
+ cs->combinator = c;
+ cs->notMatchingBefore = -1;
+ cs->selector = new CssSimpleSelector ();
+}
+
+/**
+ * \brief Return the specificity of the selector.
+ *
+ * The specificity of a CSS selector is defined in
+ * http://www.w3.org/TR/CSS21/cascade.html#specificity
+ */
+int CssSelector::specificity () {
+ int spec = 0;
+
+ for (int i = 0; i < selectorList->size (); i++)
+ spec += selectorList->getRef (i)->selector->specificity ();
+
+ return spec;
+}
+
+void CssSelector::print () {
+ for (int i = 0; i < selectorList->size (); i++) {
+ selectorList->getRef (i)->selector->print ();
+
+ if (i < selectorList->size () - 1) {
+ switch (selectorList->getRef (i + 1)->combinator) {
+ case CHILD:
+ fprintf (stderr, "> ");
+ break;
+ case DESCENDANT:
+ fprintf (stderr, "\" \" ");
+ break;
+ default:
+ fprintf (stderr, "? ");
+ break;
+ }
+ }
+ }
+
+ fprintf (stderr, "\n");
+}
+
+CssSimpleSelector::CssSimpleSelector () {
+ element = ELEMENT_ANY;
+ klass = NULL;
+ id = NULL;
+ pseudo = NULL;
+}
+
+CssSimpleSelector::~CssSimpleSelector () {
+ if (klass) {
+ for (int i = 0; i < klass->size (); i++)
+ dFree (klass->get (i));
+ delete klass;
+ }
+ dFree (id);
+ dFree (pseudo);
+}
+
+void CssSimpleSelector::setSelect (SelectType t, const char *v) {
+ switch (t) {
+ case SELECT_CLASS:
+ if (klass == NULL)
+ klass = new lout::misc::SimpleVector <char *> (1);
+ klass->increase ();
+ klass->set (klass->size () - 1, dStrdup (v));
+ break;
+ case SELECT_PSEUDO_CLASS:
+ if (pseudo == NULL)
+ pseudo = dStrdup (v);
+ break;
+ case SELECT_ID:
+ if (id == NULL)
+ id = dStrdup (v);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * \brief Return whether simple selector matches at a given node of
+ * the document tree.
+ */
+bool CssSimpleSelector::match (const DoctreeNode *n) {
+ if (element != ELEMENT_ANY && element != n->element)
+ return false;
+ if (pseudo != NULL &&
+ (n->pseudo == NULL || dStrcasecmp (pseudo, n->pseudo) != 0))
+ return false;
+ if (id != NULL && (n->id == NULL || dStrcasecmp (id, n->id) != 0))
+ return false;
+ if (klass != NULL) {
+ for (int i = 0; i < klass->size (); i++) {
+ bool found = false;
+ if (n->klass != NULL) {
+ for (int j = 0; j < n->klass->size (); j++) {
+ if (dStrcasecmp (klass->get(i), n->klass->get(j)) == 0) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (! found)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * \brief Return the specificity of the simple selector.
+ *
+ * The result is used in CssSelector::specificity ().
+ */
+int CssSimpleSelector::specificity () {
+ int spec = 0;
+
+ if (id)
+ spec += 1 << 20;
+ if (klass)
+ spec += klass->size() << 10;
+ if (pseudo)
+ spec += 1 << 10;
+ if (element != ELEMENT_ANY)
+ spec += 1;
+
+ return spec;
+}
+
+void CssSimpleSelector::print () {
+ fprintf (stderr, "Element %d, pseudo %s, id %s ",
+ element, pseudo, id);
+ if (klass != NULL) {
+ fprintf (stderr, "class ");
+ for (int i = 0; i < klass->size (); i++)
+ fprintf (stderr, ".%s", klass->get (i));
+ }
+}
+
+CssRule::CssRule (CssSelector *selector, CssPropertyList *props, int pos) {
+ assert (selector->size () > 0);
+
+ this->selector = selector;
+ this->selector->ref ();
+ this->props = props;
+ this->props->ref ();
+ this->pos = pos;
+ spec = selector->specificity ();
+};
+
+CssRule::~CssRule () {
+ selector->unref ();
+ props->unref ();
+};
+
+void CssRule::apply (CssPropertyList *props,
+ Doctree *docTree, const DoctreeNode *node) {
+ if (selector->match (docTree, node))
+ this->props->apply (props);
+}
+
+void CssRule::print () {
+ selector->print ();
+ props->print ();
+}
+
+/*
+ * \brief Insert rule with increasing specificity.
+ *
+ * If two rules have the same specificity, the one that was added later
+ * will be added behind the others.
+ * This gives later added rules more weight.
+ */
+void CssStyleSheet::RuleList::insert (CssRule *rule) {
+ increase ();
+ int i = size () - 1;
+
+ while (i > 0 && rule->specificity () < get (i - 1)->specificity ()) {
+ *getRef (i) = get (i - 1);
+ i--;
+ }
+
+ *getRef (i) = rule;
+}
+
+CssStyleSheet::CssStyleSheet () {
+ for (int i = 0; i < ntags; i++)
+ elementTable[i] = new RuleList ();
+
+ idTable = new RuleMap ();
+ classTable = new RuleMap ();
+ anyTable = new RuleList ();
+}
+
+CssStyleSheet::~CssStyleSheet () {
+ for (int i = 0; i < ntags; i++)
+ delete elementTable[i];
+ delete idTable;
+ delete classTable;
+ delete anyTable;
+}
+
+/**
+ * \brief Insert a rule into CssStyleSheet.
+ *
+ * To improve matching performance the rules are organized into
+ * rule lists based on the topmost simple selector of their selector.
+ */
+void CssStyleSheet::addRule (CssRule *rule) {
+ CssSimpleSelector *top = rule->selector->top ();
+ RuleList *ruleList = NULL;
+ lout::object::ConstString *string;
+
+ if (top->getId ()) {
+ string = new lout::object::ConstString (top->getId ());
+ ruleList = idTable->get (string);
+ if (ruleList == NULL) {
+ ruleList = new RuleList ();
+ idTable->put (string, ruleList);
+ } else {
+ delete string;
+ }
+ } else if (top->getClass () && top->getClass ()->size () > 0) {
+ string = new lout::object::ConstString (top->getClass ()->get (0));
+ ruleList = classTable->get (string);
+ if (ruleList == NULL) {
+ ruleList = new RuleList;
+ classTable->put (string, ruleList);
+ } else {
+ delete string;
+ }
+ } else if (top->getElement () >= 0 && top->getElement () < ntags) {
+ ruleList = elementTable[top->getElement ()];
+ } else if (top->getElement () == CssSimpleSelector::ELEMENT_ANY) {
+ ruleList = anyTable;
+ }
+
+ if (ruleList) {
+ ruleList->insert (rule);
+ } else {
+ assert (top->getElement () == CssSimpleSelector::ELEMENT_NONE);
+ delete rule;
+ }
+}
+
+/**
+ * \brief Apply a stylesheet to a property list.
+ *
+ * The properties are set as defined by the rules in the stylesheet that
+ * match at the given node in the document tree.
+ */
+void CssStyleSheet::apply (CssPropertyList *props,
+ Doctree *docTree, const DoctreeNode *node) {
+ static const int maxLists = 32;
+ RuleList *ruleList[maxLists];
+ int numLists = 0, index[maxLists] = {0};
+
+ if (node->id) {
+ lout::object::ConstString idString (node->id);
+
+ ruleList[numLists] = idTable->get (&idString);
+ if (ruleList[numLists])
+ numLists++;
+ }
+
+ if (node->klass) {
+ for (int i = 0; i < node->klass->size (); i++) {
+ if (i >= maxLists - 4) {
+ MSG_WARN("Maximum number of classes per element exceeded.\n");
+ break;
+ }
+
+ lout::object::ConstString classString (node->klass->get (i));
+
+ ruleList[numLists] = classTable->get (&classString);
+ if (ruleList[numLists])
+ numLists++;
+ }
+ }
+
+ ruleList[numLists] = elementTable[docTree->top ()->element];
+ if (ruleList[numLists])
+ numLists++;
+
+ ruleList[numLists] = anyTable;
+ if (ruleList[numLists])
+ numLists++;
+
+ // Apply potentially matching rules from ruleList[0-numLists] with
+ // ascending specificity.
+ // If specificity is equal, rules are applied in order of appearance.
+ // Each ruleList is sorted already.
+ while (true) {
+ int minSpec = 1 << 30;
+ int minPos = 1 << 30;
+ int minSpecIndex = -1;
+
+ for (int i = 0; i < numLists; i++) {
+ if (ruleList[i] && ruleList[i]->size () > index[i] &&
+ (ruleList[i]->get(index[i])->specificity () < minSpec ||
+ (ruleList[i]->get(index[i])->specificity () == minSpec &&
+ ruleList[i]->get(index[i])->position () < minPos))) {
+
+ minSpec = ruleList[i]->get(index[i])->specificity ();
+ minPos = ruleList[i]->get(index[i])->position ();
+ minSpecIndex = i;
+ }
+ }
+
+ if (minSpecIndex >= 0) {
+ ruleList[minSpecIndex]->get (index[minSpecIndex])->apply
+ (props, docTree, node);
+ index[minSpecIndex]++;
+ } else {
+ break;
+ }
+ }
+}
+
+CssStyleSheet *CssContext::userAgentStyle;
+CssStyleSheet *CssContext::userStyle;
+CssStyleSheet *CssContext::userImportantStyle;
+
+CssContext::CssContext () {
+ pos = 0;
+
+ for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++)
+ sheet[o] = NULL;
+
+ if (userAgentStyle == NULL) {
+ userAgentStyle = new CssStyleSheet ();
+ userStyle = new CssStyleSheet ();
+ userImportantStyle = new CssStyleSheet ();
+
+ sheet[CSS_PRIMARY_USER_AGENT] = userAgentStyle;
+ sheet[CSS_PRIMARY_USER] = userStyle;
+ sheet[CSS_PRIMARY_USER_IMPORTANT] = userImportantStyle;
+
+ buildUserAgentStyle ();
+ buildUserStyle ();
+ }
+
+ sheet[CSS_PRIMARY_USER_AGENT] = userAgentStyle;
+ sheet[CSS_PRIMARY_USER] = userStyle;
+ sheet[CSS_PRIMARY_USER_IMPORTANT] = userImportantStyle;
+}
+
+CssContext::~CssContext () {
+ for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++)
+ if (sheet[o] != userAgentStyle && sheet[o] != userStyle &&
+ sheet[o] != userImportantStyle)
+ delete sheet[o];
+}
+
+/**
+ * \brief Apply a CSS context to a property list.
+ *
+ * The stylesheets in the context are applied one after the other
+ * in the ordering defined by CSS 2.1.
+ * Stylesheets that are applied later can overwrite properties set
+ * by previous stylesheets.
+ * This allows e.g. user styles to overwrite author styles.
+ */
+void CssContext::apply (CssPropertyList *props, Doctree *docTree,
+ CssPropertyList *tagStyle, CssPropertyList *nonCssHints) {
+ const DoctreeNode *node = docTree->top ();
+
+ if (sheet[CSS_PRIMARY_USER_AGENT])
+ sheet[CSS_PRIMARY_USER_AGENT]->apply (props, docTree, node);
+
+ if (sheet[CSS_PRIMARY_USER])
+ sheet[CSS_PRIMARY_USER]->apply (props, docTree, node);
+
+ if (nonCssHints)
+ nonCssHints->apply (props);
+
+ if (sheet[CSS_PRIMARY_AUTHOR])
+ sheet[CSS_PRIMARY_AUTHOR]->apply (props, docTree, node);
+
+ if (tagStyle)
+ tagStyle->apply (props);
+
+ if (sheet[CSS_PRIMARY_AUTHOR_IMPORTANT])
+ sheet[CSS_PRIMARY_AUTHOR_IMPORTANT]->apply (props, docTree, node);
+
+ if (sheet[CSS_PRIMARY_USER_IMPORTANT])
+ sheet[CSS_PRIMARY_USER_IMPORTANT]->apply (props, docTree, node);
+}
+
+void CssContext::addRule (CssSelector *sel, CssPropertyList *props,
+ CssPrimaryOrder order) {
+
+ if (props->size () > 0) {
+ CssRule *rule = new CssRule (sel, props, pos++);
+
+ if (sheet[order] == NULL)
+ sheet[order] = new CssStyleSheet ();
+
+ sheet[order]->addRule (rule);
+ }
+}
+
+/**
+ * \brief Create the user agent style.
+ *
+ * The user agent style defines how dillo renders HTML in the absence of
+ * author or user styles.
+ */
+void CssContext::buildUserAgentStyle () {
+ const char *cssBuf =
+ "body {background-color: #e0e0a3; font-family: sans-serif; color: black;"
+ " margin: 5px}"
+ "big {font-size: 1.17em}"
+ "blockquote, dd {margin-left: 40px; margin-right: 40px}"
+ "center {text-align: center}"
+ "dt {font-weight: bolder}"
+ ":link {color: blue; text-decoration: underline; cursor: pointer}"
+ ":visited {color: #800080; text-decoration: underline; cursor: pointer}"
+ "h1, h2, h3, h4, h5, h6, b, strong {font-weight: bolder}"
+ "i, em, cite, address, var {font-style: italic}"
+ ":link img, :visited img {border: 1px solid}"
+ "frameset, ul, ol, dir {margin-left: 40px}"
+ "h1 {font-size: 2em; margin-top: .67em; margin-bottom: 0}"
+ "h2 {font-size: 1.5em; margin-top: .75em; margin-bottom: 0}"
+ "h3 {font-size: 1.17em; margin-top: .83em; margin-bottom: 0}"
+ "h4 {margin-top: 1.12em; margin-bottom: 0}"
+ "h5 {font-size: 0.83em; margin-top: 1.5em; margin-bottom: 0}"
+ "h6 {font-size: 0.75em; margin-top: 1.67em; margin-bottom: 0}"
+ "hr {width: 100%; border: 1px inset}"
+ "li {margin-top: 0.1em}"
+ "pre {white-space: pre}"
+ "ol {list-style-type: decimal}"
+ "ul {list-style-type: disc}"
+ "ul ul {list-style-type: circle}"
+ "ul ul ul {list-style-type: square}"
+ "ul ul ul ul {list-style-type: disc}"
+ "u {text-decoration: underline}"
+ "small, sub, sup {font-size: 0.83em}"
+ "sub {vertical-align: sub}"
+ "sup {vertical-align: super}"
+ "s, strike, del {text-decoration: line-through}"
+ "table {border-style: outset; border-spacing: 1px}"
+ "td, th {border-style: inset; padding: 2px}"
+ "thead, tbody, tfoot {vertical-align: middle}"
+ "th {font-weight: bolder; text-align: center}"
+ "code, tt, pre, samp, kbd {font-family: monospace}"
+ /* WORKAROUND: Reset font properties in tables as some
+ * some pages rely on it (e.g. gmail).
+ * http://developer.mozilla.org/En/Fixing_Table_Inheritance_in_Quirks_Mode
+ * has a detailed description of the issue.
+ */
+ "table, caption {font-size: medium; font-weight: normal}";
+
+ CssParser::parse (NULL, NULL, this, cssBuf, strlen (cssBuf),
+ CSS_ORIGIN_USER_AGENT);
+}
+
+void CssContext::buildUserStyle () {
+ Dstr *style;
+ char *filename = dStrconcat(dGethomedir(), "/.dillo/style.css", NULL);
+
+ if ((style = a_Misc_file2dstr(filename))) {
+ CssParser::parse (NULL,NULL,this,style->str, style->len,CSS_ORIGIN_USER);
+ dStr_free (style, 1);
+ }
+ dFree (filename);
+}
diff --git a/src/css.hh b/src/css.hh
new file mode 100644
index 00000000..b23eb9a3
--- /dev/null
+++ b/src/css.hh
@@ -0,0 +1,495 @@
+#ifndef __CSS_HH__
+#define __CSS_HH__
+
+#include "dw/core.hh"
+#include "doctree.hh"
+
+/* Origin and weight. Used only internally.*/
+typedef enum {
+ CSS_PRIMARY_USER_AGENT,
+ CSS_PRIMARY_USER,
+ CSS_PRIMARY_AUTHOR,
+ CSS_PRIMARY_AUTHOR_IMPORTANT,
+ CSS_PRIMARY_USER_IMPORTANT,
+ CSS_PRIMARY_LAST,
+} CssPrimaryOrder;
+
+typedef enum {
+ CSS_ORIGIN_USER_AGENT,
+ CSS_ORIGIN_USER,
+ CSS_ORIGIN_AUTHOR,
+} CssOrigin;
+
+typedef enum {
+ CSS_TYPE_INTEGER, /* This type is only used internally, for x-*
+ properties. */
+ CSS_TYPE_ENUM, /* Value is i, if represented by
+ enum_symbols[i]. */
+ CSS_TYPE_MULTI_ENUM, /* For all enum_symbols[i], 1 << i are
+ combined. */
+ CSS_TYPE_LENGTH_PERCENTAGE, /* <length> or <percentage>. Represented by
+ CssLength. */
+ CSS_TYPE_LENGTH, /* <length>, represented as CssLength.
+ Note: In some cases, CSS_TYPE_LENGTH is used
+ instead of CSS_TYPE_LENGTH_PERCENTAGE,
+ only because Dw cannot handle percentages
+ in this particular case (e.g.
+ 'margin-*-width'). */
+ CSS_TYPE_SIGNED_LENGTH, /* As CSS_TYPE_LENGTH but may be negative. */
+ CSS_TYPE_LENGTH_PERCENTAGE_NUMBER, /* <length> or <percentage>, or <number> */
+ CSS_TYPE_COLOR, /* Represented as integer. */
+ CSS_TYPE_FONT_WEIGHT, /* this very special and only used by
+ 'font-weight' */
+ CSS_TYPE_STRING, /* <string> */
+ CSS_TYPE_SYMBOL, /* Symbols, which are directly copied (as
+ opposed to CSS_TYPE_ENUM and
+ CSS_TYPE_MULTI_ENUM). Used for
+ 'font-family'. */
+ CSS_TYPE_UNUSED /* Not yet used. Will itself get unused some
+ day. */
+} CssValueType;
+
+/*
+ * Lengths are represented as int in the following way:
+ *
+ * | <------ integer value ------> |
+ *
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ * | integer part | type |
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ * | integer part | decimal fraction | type |
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ * n-1 15 14 3 2 1 0
+ *
+ * | <------ fixed point value ------> |
+ *
+ * where type is one of the CSS_LENGTH_TYPE_* values.
+ * CSS_LENGTH_TYPE_PX values are stored as
+ * 29 bit signed integer, all other types as fixed point values.
+ */
+
+typedef int CssLength;
+
+typedef enum {
+ CSS_LENGTH_TYPE_NONE,
+ CSS_LENGTH_TYPE_PX,
+ CSS_LENGTH_TYPE_MM, /* "cm", "in", "pt" and "pc" are converted into
+ millimeters. */
+ CSS_LENGTH_TYPE_EM,
+ CSS_LENGTH_TYPE_EX,
+ CSS_LENGTH_TYPE_PERCENTAGE,
+ CSS_LENGTH_TYPE_RELATIVE, /* This does not exist in CSS but
+ is used in HTML */
+ CSS_LENGTH_TYPE_AUTO /* This can be used as a simple value. */
+} CssLengthType;
+
+inline CssLength CSS_CREATE_LENGTH (float v, CssLengthType t) {
+ static const int CSS_LENGTH_FRAC_MAX = (1 << (32 - 15 - 1)) - 1;
+ static const int CSS_LENGTH_INT_MAX = (1 << (32 - 4)) - 1;
+ int iv;
+
+ switch (t) {
+ case CSS_LENGTH_TYPE_PX:
+ iv = (int) (v + 0.5);
+ if (iv > CSS_LENGTH_INT_MAX)
+ iv = CSS_LENGTH_INT_MAX;
+ else if (iv < -CSS_LENGTH_INT_MAX)
+ iv = -CSS_LENGTH_INT_MAX;
+ return iv << 3 | t;
+ case CSS_LENGTH_TYPE_NONE:
+ case CSS_LENGTH_TYPE_MM:
+ case CSS_LENGTH_TYPE_EM:
+ case CSS_LENGTH_TYPE_EX:
+ case CSS_LENGTH_TYPE_PERCENTAGE:
+ case CSS_LENGTH_TYPE_RELATIVE:
+ if (v > CSS_LENGTH_FRAC_MAX)
+ v = CSS_LENGTH_FRAC_MAX;
+ else if (v < -CSS_LENGTH_FRAC_MAX)
+ v = -CSS_LENGTH_FRAC_MAX;
+ return ((int) (v * (1 << 15)) & ~7 ) | t;
+ case CSS_LENGTH_TYPE_AUTO:
+ return t;
+ default:
+ assert(false);
+ return CSS_LENGTH_TYPE_AUTO;
+ }
+}
+
+inline CssLengthType CSS_LENGTH_TYPE (CssLength l) {
+ return (CssLengthType) (l & 7);
+}
+
+inline float CSS_LENGTH_VALUE (CssLength l) {
+ switch (CSS_LENGTH_TYPE(l)) {
+ case CSS_LENGTH_TYPE_PX:
+ return (float) (l >> 3);
+ case CSS_LENGTH_TYPE_NONE:
+ case CSS_LENGTH_TYPE_MM:
+ case CSS_LENGTH_TYPE_EM:
+ case CSS_LENGTH_TYPE_EX:
+ case CSS_LENGTH_TYPE_PERCENTAGE:
+ case CSS_LENGTH_TYPE_RELATIVE:
+ return ((float)(l & ~7)) / (1 << 15);
+ case CSS_LENGTH_TYPE_AUTO:
+ return 0.0;
+ default:
+ assert(false);
+ return 0.0;
+ }
+}
+
+typedef enum {
+ CSS_PROPERTY_BACKGROUND_ATTACHMENT,
+ CSS_PROPERTY_BACKGROUND_COLOR,
+ CSS_PROPERTY_BACKGROUND_IMAGE,
+ CSS_PROPERTY_BACKGROUND_POSITION,
+ CSS_PROPERTY_BACKGROUND_REPEAT,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_COLLAPSE,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_PROPERTY_BORDER_SPACING,
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BOTTOM,
+ CSS_PROPERTY_CAPTION_SIDE,
+ CSS_PROPERTY_CLEAR,
+ CSS_PROPERTY_CLIP,
+ CSS_PROPERTY_COLOR,
+ CSS_PROPERTY_CONTENT,
+ CSS_PROPERTY_COUNTER_INCREMENT,
+ CSS_PROPERTY_COUNTER_RESET,
+ CSS_PROPERTY_CURSOR,
+ CSS_PROPERTY_DIRECTION,
+ CSS_PROPERTY_DISPLAY,
+ CSS_PROPERTY_EMPTY_CELLS,
+ CSS_PROPERTY_FLOAT,
+ CSS_PROPERTY_FONT_FAMILY,
+ CSS_PROPERTY_FONT_SIZE,
+ CSS_PROPERTY_FONT_SIZE_ADJUST,
+ CSS_PROPERTY_FONT_STRETCH,
+ CSS_PROPERTY_FONT_STYLE,
+ CSS_PROPERTY_FONT_VARIANT,
+ CSS_PROPERTY_FONT_WEIGHT,
+ CSS_PROPERTY_HEIGHT,
+ CSS_PROPERTY_LEFT,
+ CSS_PROPERTY_LETTER_SPACING,
+ CSS_PROPERTY_LINE_HEIGHT,
+ CSS_PROPERTY_LIST_STYLE_IMAGE,
+ CSS_PROPERTY_LIST_STYLE_POSITION,
+ CSS_PROPERTY_LIST_STYLE_TYPE,
+ CSS_PROPERTY_MARGIN_BOTTOM,
+ CSS_PROPERTY_MARGIN_LEFT,
+ CSS_PROPERTY_MARGIN_RIGHT,
+ CSS_PROPERTY_MARGIN_TOP,
+ CSS_PROPERTY_MARKER_OFFSET,
+ CSS_PROPERTY_MARKS,
+ CSS_PROPERTY_MAX_HEIGHT,
+ CSS_PROPERTY_MAX_WIDTH,
+ CSS_PROPERTY_MIN_HEIGHT,
+ CSS_PROPERTY_MIN_WIDTH,
+ CSS_PROPERTY_OUTLINE_COLOR,
+ CSS_PROPERTY_OUTLINE_STYLE,
+ CSS_PROPERTY_OUTLINE_WIDTH,
+ CSS_PROPERTY_OVERFLOW,
+ CSS_PROPERTY_PADDING_BOTTOM,
+ CSS_PROPERTY_PADDING_LEFT,
+ CSS_PROPERTY_PADDING_RIGHT,
+ CSS_PROPERTY_PADDING_TOP,
+ CSS_PROPERTY_POSITION,
+ CSS_PROPERTY_QUOTES,
+ CSS_PROPERTY_RIGHT,
+ CSS_PROPERTY_TEXT_ALIGN,
+ CSS_PROPERTY_TEXT_DECORATION,
+ CSS_PROPERTY_TEXT_INDENT,
+ CSS_PROPERTY_TEXT_SHADOW,
+ CSS_PROPERTY_TEXT_TRANSFORM,
+ CSS_PROPERTY_TOP,
+ CSS_PROPERTY_UNICODE_BIDI,
+ CSS_PROPERTY_VERTICAL_ALIGN,
+ CSS_PROPERTY_VISIBILITY,
+ CSS_PROPERTY_WHITE_SPACE,
+ CSS_PROPERTY_WIDTH,
+ CSS_PROPERTY_WORD_SPACING,
+ CSS_PROPERTY_Z_INDEX,
+ CSS_PROPERTY_X_LINK,
+ CSS_PROPERTY_X_COLSPAN,
+ CSS_PROPERTY_X_ROWSPAN,
+ PROPERTY_X_LINK,
+ PROPERTY_X_IMG,
+ PROPERTY_X_TOOLTIP,
+ CSS_PROPERTY_LAST
+} CssPropertyName;
+
+typedef union {
+ int32_t intVal;
+ char *strVal;
+} CssPropertyValue;
+
+typedef enum {
+ CSS_BORDER_WIDTH_THIN,
+ CSS_BORDER_WIDTH_MEDIUM,
+ CSS_BORDER_WIDTH_THICK,
+} CssBorderWidthExtensions;
+
+typedef enum {
+ CSS_FONT_WEIGHT_BOLD,
+ CSS_FONT_WEIGHT_BOLDER,
+ CSS_FONT_WEIGHT_LIGHT,
+ CSS_FONT_WEIGHT_LIGHTER,
+ CSS_FONT_WEIGHT_NORMAL,
+} CssFontWeightExtensions;
+
+typedef enum {
+ CSS_FONT_SIZE_LARGE,
+ CSS_FONT_SIZE_LARGER,
+ CSS_FONT_SIZE_MEDIUM,
+ CSS_FONT_SIZE_SMALL,
+ CSS_FONT_SIZE_SMALLER,
+ CSS_FONT_SIZE_XX_LARGE,
+ CSS_FONT_SIZE_XX_SMALL,
+ CSS_FONT_SIZE_X_LARGE,
+ CSS_FONT_SIZE_X_SMALL,
+} CssFontSizeExtensions;
+
+typedef enum {
+ CSS_LETTER_SPACING_NORMAL
+} CssLetterSpacingExtensions;
+
+typedef enum {
+ CSS_WORD_SPACING_NORMAL
+} CssWordSpacingExtensions;
+
+
+/**
+ * \brief This class holds a CSS property and value pair.
+ */
+class CssProperty {
+ public:
+
+ short name;
+ short type;
+ CssPropertyValue value;
+
+ inline void free () {
+ switch (type) {
+ case CSS_TYPE_STRING:
+ case CSS_TYPE_SYMBOL:
+ dFree (value.strVal);
+ break;
+ default:
+ break;
+ }
+ }
+ void print ();
+};
+
+/**
+ * \brief A list of CssProperty objects.
+ */
+class CssPropertyList : public lout::misc::SimpleVector <CssProperty> {
+ int refCount;
+ bool ownerOfStrings;
+
+ public:
+ inline CssPropertyList(bool ownerOfStrings = false) :
+ lout::misc::SimpleVector <CssProperty> (1) {
+ refCount = 0;
+ this->ownerOfStrings = ownerOfStrings;
+ };
+ inline CssPropertyList(const CssPropertyList &p) :
+ lout::misc::SimpleVector <CssProperty> (p) {
+ refCount = 0;
+ ownerOfStrings = false;
+ };
+ ~CssPropertyList ();
+
+ void set (CssPropertyName name, CssValueType type,
+ CssPropertyValue value);
+ inline void set (CssPropertyName name, CssValueType type, char *value) {
+ CssPropertyValue v;
+ v.strVal = value;
+ set (name, type, v);
+ };
+ inline void set (CssPropertyName name, CssValueType type, int value) {
+ CssPropertyValue v;
+ v.intVal = value;
+ set (name, type, v);
+ };
+ void apply (CssPropertyList *props);
+ void print ();
+ inline void ref () { refCount++; }
+ inline void unref () { if (--refCount == 0) delete this; }
+};
+
+class CssSimpleSelector {
+ private:
+ int element;
+ char *pseudo, *id;
+ lout::misc::SimpleVector <char *> *klass;
+
+ public:
+ enum {
+ ELEMENT_NONE = -1,
+ ELEMENT_ANY = -2,
+ };
+
+ typedef enum {
+ SELECT_NONE,
+ SELECT_CLASS,
+ SELECT_PSEUDO_CLASS,
+ SELECT_ID,
+ } SelectType;
+
+ CssSimpleSelector ();
+ ~CssSimpleSelector ();
+ inline void setElement (int e) { element = e; };
+ void setSelect (SelectType t, const char *v);
+ inline lout::misc::SimpleVector <char *> *getClass () { return klass; };
+ inline const char *getPseudoClass () { return pseudo; };
+ inline const char *getId () { return id; };
+ inline int getElement () { return element; };
+ bool match (const DoctreeNode *node);
+ int specificity ();
+ void print ();
+};
+
+/**
+ * \brief CSS selector class.
+ *
+ * \todo Implement missing selector options.
+ */
+class CssSelector {
+ public:
+ typedef enum {
+ DESCENDANT,
+ CHILD,
+ ADJACENT_SIBLING,
+ } Combinator;
+
+ private:
+ struct CombinatorAndSelector {
+ int notMatchingBefore; // used for optimizing CSS selector matching
+ Combinator combinator;
+ CssSimpleSelector *selector;
+ };
+
+ int refCount;
+ lout::misc::SimpleVector <struct CombinatorAndSelector> *selectorList;
+
+ public:
+ CssSelector ();
+ ~CssSelector ();
+ void addSimpleSelector (Combinator c);
+ inline CssSimpleSelector *top () {
+ return selectorList->getRef (selectorList->size () - 1)->selector;
+ };
+ inline int size () { return selectorList->size (); };
+ bool match (Doctree *dt, const DoctreeNode *node);
+ int specificity ();
+ void print ();
+ inline void ref () { refCount++; }
+ inline void unref () { if (--refCount == 0) delete this; }
+};
+
+/**
+ * \brief A CssSelector CssPropertyList pair.
+ *
+ * The CssPropertyList is applied if the CssSelector matches.
+ */
+class CssRule {
+ private:
+ CssPropertyList *props;
+ int spec, pos;
+
+ public:
+ CssSelector *selector;
+
+ CssRule (CssSelector *selector, CssPropertyList *props, int pos);
+ ~CssRule ();
+
+ void apply (CssPropertyList *props,
+ Doctree *docTree, const DoctreeNode *node);
+ inline int specificity () { return spec; };
+ inline int position () { return pos; };
+ void print ();
+};
+
+/**
+ * \brief A list of CssRules.
+ *
+ * In apply () all matching rules are applied.
+ */
+class CssStyleSheet {
+ private:
+ class RuleList : public lout::misc::SimpleVector <CssRule*>,
+ public lout::object::Object {
+ public:
+ RuleList () : lout::misc::SimpleVector <CssRule*> (1) {};
+ ~RuleList () {
+ for (int i = 0; i < size (); i++)
+ delete get (i);
+ };
+
+ void insert (CssRule *rule);
+ inline bool equals (lout::object::Object *other) {
+ return this == other;
+ };
+ inline int hashValue () { return (intptr_t) this; };
+ };
+
+ class RuleMap : public lout::container::typed::HashTable
+ <lout::object::ConstString, RuleList > {
+ public:
+ RuleMap () : lout::container::typed::HashTable
+ <lout::object::ConstString, RuleList > (true, true, 256) {};
+ };
+
+ static const int ntags = 90; // \todo replace 90
+ RuleList *elementTable[ntags];
+
+ RuleMap *idTable;
+ RuleMap *classTable;
+ RuleList *anyTable;
+
+ public:
+ CssStyleSheet();
+ ~CssStyleSheet();
+ void addRule (CssRule *rule);
+ void apply (CssPropertyList *props,
+ Doctree *docTree, const DoctreeNode *node);
+};
+
+/**
+ * \brief A set of CssStyleSheets.
+ */
+class CssContext {
+ private:
+ static CssStyleSheet *userAgentStyle;
+ static CssStyleSheet *userStyle;
+ static CssStyleSheet *userImportantStyle;
+ CssStyleSheet *sheet[CSS_PRIMARY_USER_IMPORTANT + 1];
+ int pos;
+
+ void buildUserAgentStyle ();
+ void buildUserStyle ();
+
+ public:
+ CssContext ();
+ ~CssContext ();
+
+ void addRule (CssSelector *sel, CssPropertyList *props,
+ CssPrimaryOrder order);
+ void apply (CssPropertyList *props,
+ Doctree *docTree,
+ CssPropertyList *tagStyle, CssPropertyList *nonCssHints);
+};
+
+#endif
diff --git a/src/cssparser.cc b/src/cssparser.cc
new file mode 100644
index 00000000..00ba7428
--- /dev/null
+++ b/src/cssparser.cc
@@ -0,0 +1,1476 @@
+/*
+ * File: cssparser.cc
+ *
+ * Copyright 2004 Sebastian Geerken <sgeerken@dillo.org>
+ * Copyright 2008-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * This file is heavily based on the CSS parser of dillo-0.8.0-css-3 -
+ * a dillo1 based CSS prototype written by Sebastian Geerken.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "msg.h"
+#include "colors.h"
+#include "html_common.hh"
+#include "css.hh"
+#include "cssparser.hh"
+
+using namespace dw::core::style;
+
+#define DEBUG_MSG(A, B, ...) _MSG(B, __VA_ARGS__)
+#define MSG_CSS(A, ...) MSG(A, __VA_ARGS__)
+#define DEBUG_TOKEN_LEVEL 0
+#define DEBUG_PARSE_LEVEL 0
+#define DEBUG_CREATE_LEVEL 0
+
+#define DEBUG_LEVEL 10
+
+/* The last three ones are never parsed. */
+#define CSS_NUM_INTERNAL_PROPERTIES 3
+#define CSS_NUM_PARSED_PROPERTIES \
+ (CSS_PROPERTY_LAST - CSS_NUM_INTERNAL_PROPERTIES)
+
+
+typedef struct {
+ const char *symbol;
+ const CssValueType type[3];
+ const char *const *enum_symbols;
+} CssPropertyInfo;
+
+static const char *const Css_border_style_enum_vals[] = {
+ "none", "hidden", "dotted", "dashed", "solid", "double", "groove",
+ "ridge", "inset", "outset", NULL
+};
+
+static const char *const Css_border_width_enum_vals[] = {
+ "thin", "medium", "thick", NULL
+};
+
+static const char *const Css_cursor_enum_vals[] = {
+ "crosshair", "default", "pointer", "move", "e-resize", "ne-resize",
+ "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize",
+ "w-resize", "text", "wait", "help", NULL
+};
+
+static const char *const Css_display_enum_vals[] = {
+ "block", "inline", "list-item", "none", "table", "table-row-group",
+ "table-header-group", "table-footer-group", "table-row",
+ "table-cell", NULL
+};
+
+static const char *const Css_font_size_enum_vals[] = {
+ "large", "larger", "medium", "small", "smaller", "xx-large", "xx-small",
+ "x-large", "x-small", NULL
+};
+
+static const char *const Css_font_style_enum_vals[] = {
+ "normal", "italic", "oblique", NULL
+};
+
+static const char *const Css_font_weight_enum_vals[] = {
+ "bold", "bolder", "light", "lighter", "normal", NULL
+};
+
+static const char *const Css_letter_spacing_enum_vals[] = {
+ "normal", NULL
+};
+
+static const char *const Css_list_style_position_enum_vals[] = {
+ "inside", "outside", NULL
+};
+
+static const char *const Css_line_height_enum_vals[] = {
+ "normal", NULL
+};
+
+static const char *const Css_list_style_type_enum_vals[] = {
+ "disc", "circle", "square", "decimal", "decimal-leading-zero",
+ "lower-roman", "upper-roman", "lower-greek", "lower-alpha",
+ "lower-latin", "upper-alpha", "upper-latin", "hebrew", "armenian",
+ "georgian", "cjk-ideographic", "hiragana", "katakana", "hiragana-iroha",
+ "katakana-iroha", "none", NULL
+};
+
+static const char *const Css_text_align_enum_vals[] = {
+ "left", "right", "center", "justify", "string", NULL
+};
+
+static const char *const Css_text_decoration_enum_vals[] = {
+ "underline", "overline", "line-through", "blink", NULL
+};
+
+static const char *const Css_vertical_align_vals[] = {
+ "top", "bottom", "middle", "baseline", "sub", "super", "text-top",
+ "text-bottom", NULL
+};
+
+static const char *const Css_white_space_vals[] = {
+ "normal", "pre", "nowrap", "pre-wrap", "pre-line", NULL
+};
+
+static const char *const Css_word_spacing_enum_vals[] = {
+ "normal", NULL
+};
+
+const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
+ {"background-attachment", {CSS_TYPE_UNUSED}, NULL},
+ {"background-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"background-image", {CSS_TYPE_UNUSED}, NULL},
+ {"background-position", {CSS_TYPE_UNUSED}, NULL},
+ {"background-repeat", {CSS_TYPE_UNUSED}, NULL},
+ {"border-bottom-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-bottom-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-bottom-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"border-collapse", {CSS_TYPE_UNUSED}, NULL},
+ {"border-left-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-left-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-left-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"border-right-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-right-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-rigth-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"border-spacing", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"border-top-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-top-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-top-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"bottom", {CSS_TYPE_UNUSED}, NULL},
+ {"caption-side", {CSS_TYPE_UNUSED}, NULL},
+ {"clear", {CSS_TYPE_UNUSED}, NULL},
+ {"clip", {CSS_TYPE_UNUSED}, NULL},
+ {"color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"content", {CSS_TYPE_STRING, CSS_TYPE_UNUSED}, NULL},
+ {"counter-increment", {CSS_TYPE_UNUSED}, NULL},
+ {"counter-reset", {CSS_TYPE_UNUSED}, NULL},
+ {"cursor", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_cursor_enum_vals},
+ {"direction", {CSS_TYPE_UNUSED}, NULL},
+ {"display", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_display_enum_vals},
+ {"empty-cells", {CSS_TYPE_UNUSED}, NULL},
+ {"float", {CSS_TYPE_UNUSED}, NULL},
+ {"font-family", {CSS_TYPE_SYMBOL, CSS_TYPE_UNUSED}, NULL},
+ {"font-size", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED},
+ Css_font_size_enum_vals},
+ {"font-size-adjust", {CSS_TYPE_UNUSED}, NULL},
+ {"font-stretch", {CSS_TYPE_UNUSED}, NULL},
+ {"font-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_font_style_enum_vals},
+ {"font-variant", {CSS_TYPE_UNUSED}, NULL},
+ {"font-weight", {CSS_TYPE_ENUM, CSS_TYPE_FONT_WEIGHT, CSS_TYPE_UNUSED},
+ Css_font_weight_enum_vals},
+ {"height", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
+ {"left", {CSS_TYPE_UNUSED}, NULL},
+ {"letter-spacing", {CSS_TYPE_ENUM, CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED},
+ Css_letter_spacing_enum_vals},
+ {"line-height",
+ {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE_NUMBER, CSS_TYPE_UNUSED},
+ Css_line_height_enum_vals},
+ {"list-style-image", {CSS_TYPE_UNUSED}, NULL},
+ {"list-style-position", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_list_style_position_enum_vals},
+ {"list-style-type", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_list_style_type_enum_vals},
+ {"margin-bottom", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"margin-left", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"margin-right", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"margin-top", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"marker-offset", {CSS_TYPE_UNUSED}, NULL},
+ {"marks", {CSS_TYPE_UNUSED}, NULL},
+ {"max-height", {CSS_TYPE_UNUSED}, NULL},
+ {"max-width", {CSS_TYPE_UNUSED}, NULL},
+ {"min-height", {CSS_TYPE_UNUSED}, NULL},
+ {"min-width", {CSS_TYPE_UNUSED}, NULL},
+ {"outline-color", {CSS_TYPE_UNUSED}, NULL},
+ {"outline-style", {CSS_TYPE_UNUSED}, NULL},
+ {"outline-width", {CSS_TYPE_UNUSED}, NULL},
+ {"overflow", {CSS_TYPE_UNUSED}, NULL},
+ {"padding-bottom", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"padding-left", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"padding-right", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"padding-top", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"position", {CSS_TYPE_UNUSED}, NULL},
+ {"quotes", {CSS_TYPE_UNUSED}, NULL},
+ {"right", {CSS_TYPE_UNUSED}, NULL},
+ {"text-align", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_text_align_enum_vals},
+ {"text-decoration", {CSS_TYPE_MULTI_ENUM, CSS_TYPE_UNUSED},
+ Css_text_decoration_enum_vals},
+ {"text-indent", {CSS_TYPE_UNUSED}, NULL},
+ {"text-shadow", {CSS_TYPE_UNUSED}, NULL},
+ {"text-transform", {CSS_TYPE_UNUSED}, NULL},
+ {"top", {CSS_TYPE_UNUSED}, NULL},
+ {"unicode-bidi", {CSS_TYPE_UNUSED}, NULL},
+ {"vertical-align",{CSS_TYPE_ENUM, CSS_TYPE_UNUSED},Css_vertical_align_vals},
+ {"visibility", {CSS_TYPE_UNUSED}, NULL},
+ {"white-space", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_white_space_vals},
+ {"width", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
+ {"word-spacing", {CSS_TYPE_ENUM, CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED},
+ Css_word_spacing_enum_vals},
+ {"z-index", {CSS_TYPE_UNUSED}, NULL},
+
+ /* These are extensions, for internal used, and never parsed. */
+ {"x-link", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
+ {"x-colspan", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
+ {"x-rowspan", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
+ {"last", {CSS_TYPE_UNUSED}, NULL},
+};
+
+typedef struct {
+ const char *symbol;
+ enum {
+ CSS_SHORTHAND_MULTIPLE, /* [ p1 || p2 || ...], the property pi is
+ * determined by the type */
+ CSS_SHORTHAND_DIRECTIONS, /* <t>{1,4} */
+ CSS_SHORTHAND_BORDER, /* special, used for 'border' */
+ CSS_SHORTHAND_FONT, /* special, used for 'font' */
+ } type;
+ const CssPropertyName * properties;/* CSS_SHORTHAND_MULTIPLE:
+ * must be terminated by -1
+ * CSS_SHORTHAND_DIRECTIONS:
+ * must have length 4
+ * CSS_SHORTHAND_BORDERS:
+ * must have length 12
+ * CSS_SHORTHAND_FONT:
+ * unused */
+} CssShorthandInfo;
+
+const CssPropertyName Css_background_properties[] = {
+ CSS_PROPERTY_BACKGROUND_COLOR,
+ CSS_PROPERTY_BACKGROUND_IMAGE,
+ CSS_PROPERTY_BACKGROUND_REPEAT,
+ CSS_PROPERTY_BACKGROUND_ATTACHMENT,
+ CSS_PROPERTY_BACKGROUND_POSITION,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_bottom_properties[] = {
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_color_properties[4] = {
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR
+};
+
+const CssPropertyName Css_border_left_properties[] = {
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_right_properties[] = {
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_style_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE
+};
+
+const CssPropertyName Css_border_top_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_width_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH
+};
+
+const CssPropertyName Css_list_style_properties[] = {
+ CSS_PROPERTY_LIST_STYLE_TYPE,
+ CSS_PROPERTY_LIST_STYLE_POSITION,
+ CSS_PROPERTY_LIST_STYLE_IMAGE,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_margin_properties[] = {
+ CSS_PROPERTY_MARGIN_TOP,
+ CSS_PROPERTY_MARGIN_BOTTOM,
+ CSS_PROPERTY_MARGIN_LEFT,
+ CSS_PROPERTY_MARGIN_RIGHT
+};
+
+const CssPropertyName Css_outline_properties[] = {
+ CSS_PROPERTY_OUTLINE_COLOR,
+ CSS_PROPERTY_OUTLINE_STYLE,
+ CSS_PROPERTY_OUTLINE_WIDTH,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_padding_properties[] = {
+ CSS_PROPERTY_PADDING_TOP,
+ CSS_PROPERTY_PADDING_BOTTOM,
+ CSS_PROPERTY_PADDING_LEFT,
+ CSS_PROPERTY_PADDING_RIGHT
+};
+
+const CssPropertyName Css_border_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR
+};
+
+const CssPropertyName Css_font_properties[] = {
+ CSS_PROPERTY_FONT_SIZE,
+ CSS_PROPERTY_FONT_STYLE,
+ CSS_PROPERTY_FONT_VARIANT,
+ CSS_PROPERTY_FONT_WEIGHT,
+ CSS_PROPERTY_FONT_FAMILY,
+ (CssPropertyName) - 1
+};
+
+static const CssShorthandInfo Css_shorthand_info[] = {
+ {"background", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_background_properties},
+ {"border", CssShorthandInfo::CSS_SHORTHAND_BORDER,
+ Css_border_properties},
+ {"border-bottom", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_bottom_properties},
+ {"border-color", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_border_color_properties},
+ {"border-left", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_left_properties},
+ {"border-right", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_right_properties},
+ {"border-style", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_border_style_properties},
+ {"border-top", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_top_properties},
+ {"border-width", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_border_width_properties},
+ {"font", CssShorthandInfo::CSS_SHORTHAND_FONT,
+ Css_font_properties},
+ {"list-style", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_list_style_properties},
+ {"margin", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_margin_properties},
+ {"outline", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_outline_properties},
+ {"padding", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_padding_properties},
+};
+
+#define CSS_SHORTHAND_NUM \
+ (sizeof(Css_shorthand_info) / sizeof(CssShorthandInfo))
+
+/* ----------------------------------------------------------------------
+ * Parsing
+ * ---------------------------------------------------------------------- */
+
+CssParser::CssParser(CssContext *context, CssOrigin origin,
+ const char *buf, int buflen)
+{
+ this->context = context;
+ this->origin = origin;
+ this->buf = buf;
+ this->buflen = buflen;
+ this->bufptr = 0;
+ this->spaceSeparated = false;
+ this->withinBlock = false;
+
+ nextToken ();
+}
+
+/*
+ * Gets the next character from the buffer, or EOF.
+ */
+int CssParser::getChar()
+{
+ int c;
+
+ if (bufptr >= buflen)
+ c = EOF;
+ else
+ c = buf[bufptr];
+
+ /* The buffer pointer is increased in any case, so that ungetChar works
+ * correctly at the end of the buffer. */
+ bufptr++;
+ return c;
+}
+
+/*
+ * Undoes the last getChar().
+ */
+void CssParser::ungetChar()
+{
+ bufptr--;
+}
+
+/*
+ * Skip string str if it is found in the input buffer.
+ * If not wind back. The first char is passed as parameter c
+ * to avoid unnecessary getChar() / ungetChar() calls.
+ */
+inline bool CssParser::skipString(int c, const char *str)
+{
+ int n = 0;
+
+ while (str[n]) {
+ if (str[n] != c) {
+ while (n--)
+ ungetChar();
+ return false;
+ }
+ c = getChar();
+ n++;
+ }
+
+ return true;
+}
+
+void CssParser::nextToken()
+{
+ int c, c1, d, j;
+ char hexbuf[5];
+ int i = 0;
+
+ ttype = CSS_TK_CHAR; /* init */
+ spaceSeparated = false;
+
+ while (true) {
+ c = getChar();
+ if (isspace(c)) { // ignore whitespace
+ spaceSeparated = true;
+ } else if (skipString(c, "/*")) { // ignore comments
+ do {
+ c = getChar();
+ } while (c != EOF && ! skipString(c, "*/"));
+ } else if (skipString(c, "<!--")) { // ignore XML comment markers
+ } else if (skipString(c, "-->")) {
+ } else {
+ break;
+ }
+ }
+
+ // handle negative numbers
+ if (c == '-') {
+ if (i < maxStrLen - 1)
+ tval[i++] = c;
+ c = getChar();
+ }
+
+ if (isdigit(c)) {
+ ttype = CSS_TK_DECINT;
+ do {
+ if (i < maxStrLen - 1) {
+ tval[i++] = c;
+ }
+ /* else silently truncated */
+ c = getChar();
+ } while (isdigit(c));
+ if (c != '.')
+ ungetChar();
+
+ /* ...but keep going to see whether it's really a float */
+ }
+
+ if (c == '.') {
+ c = getChar();
+ if (isdigit(c)) {
+ ttype = CSS_TK_FLOAT;
+ if (i < maxStrLen - 1)
+ tval[i++] = '.';
+ do {
+ if (i < maxStrLen - 1)
+ tval[i++] = c;
+ /* else silently truncated */
+ c = getChar();
+ } while (isdigit(c));
+
+ ungetChar();
+ tval[i] = 0;
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token number %s\n", tval);
+ return;
+ } else {
+ ungetChar();
+ if (ttype == CSS_TK_DECINT) {
+ ungetChar();
+ } else {
+ c = '.';
+ }
+ }
+ }
+
+ if (ttype == CSS_TK_DECINT) {
+ tval[i] = 0;
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token number %s\n", tval);
+ return;
+ }
+
+ if (i) {
+ ungetChar(); /* ungetChar '-' */
+ i--;
+ c = getChar();
+ }
+
+ if (isalpha(c) || c == '_' || c == '-') {
+ ttype = CSS_TK_SYMBOL;
+
+ tval[0] = c;
+ i = 1;
+ c = getChar();
+ while (isalnum(c) || c == '_' || c == '-') {
+ if (i < maxStrLen - 1) {
+ tval[i] = c;
+ i++;
+ } /* else silently truncated */
+ c = getChar();
+ }
+ tval[i] = 0;
+ ungetChar();
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token symbol '%s'\n", tval);
+ return;
+ }
+
+ if (c == '"' || c == '\'') {
+ c1 = c;
+ ttype = CSS_TK_STRING;
+
+ i = 0;
+ c = getChar();
+
+ while (c != EOF && c != c1) {
+ if (c == '\\') {
+ d = getChar();
+ if (isxdigit(d)) {
+ /* Read hex Unicode char. (Actually, strings are yet only 8
+ * bit.) */
+ hexbuf[0] = d;
+ j = 1;
+ d = getChar();
+ while (j < 4 && isxdigit(d)) {
+ hexbuf[j] = d;
+ j++;
+ d = getChar();
+ }
+ hexbuf[j] = 0;
+ ungetChar();
+ c = strtol(hexbuf, NULL, 16);
+ } else {
+ /* Take character literally. */
+ c = d;
+ }
+ }
+
+ if (i < maxStrLen - 1) {
+ tval[i] = c;
+ i++;
+ } /* else silently truncated */
+ c = getChar();
+ }
+ tval[i] = 0;
+ /* No ungetChar(). */
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token string '%s'\n", tval);
+ return;
+ }
+
+ /*
+ * Within blocks, '#' starts a color, outside, it is used in selectors.
+ */
+ if (c == '#' && withinBlock) {
+ ttype = CSS_TK_COLOR;
+
+ tval[0] = c;
+ i = 1;
+ c = getChar();
+ while (isxdigit(c)) {
+ if (i < maxStrLen - 1) {
+ tval[i] = c;
+ i++;
+ } /* else silently truncated */
+ c = getChar();
+ }
+ tval[i] = 0;
+ ungetChar();
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token color '%s'\n", tval);
+ return;
+ }
+
+ if (c == EOF) {
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token %s\n", "EOF");
+ ttype = CSS_TK_END;
+ return;
+ }
+
+ ttype = CSS_TK_CHAR;
+ tval[0] = c;
+ tval[1] = 0;
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token char '%c'\n", c);
+}
+
+
+bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type)
+{
+ int i, err = 1;
+ CssValueType savedType = *type;
+
+ for (int j = 0; Css_property_info[prop].type[j] != CSS_TYPE_UNUSED; j++) {
+ *type = Css_property_info[prop].type[j];
+
+ switch (Css_property_info[prop].type[j]) {
+
+ case CSS_TYPE_ENUM:
+ if (ttype == CSS_TK_SYMBOL) {
+ for (i = 0; Css_property_info[prop].enum_symbols[i]; i++)
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0)
+ return true;
+ }
+ break;
+
+ case CSS_TYPE_MULTI_ENUM:
+ if (ttype == CSS_TK_SYMBOL) {
+ if (dStrcasecmp(tval, "none") == 0)
+ return true;
+ else {
+ for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) {
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0)
+ return true;
+ }
+ }
+ }
+ break;
+
+ case CSS_TYPE_LENGTH_PERCENTAGE:
+ case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER:
+ case CSS_TYPE_LENGTH:
+ if (tval[0] == '-')
+ return false;
+ // Fall Through
+ case CSS_TYPE_SIGNED_LENGTH:
+ if (ttype == CSS_TK_DECINT ||
+ ttype == CSS_TK_FLOAT ||
+ (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0))
+ return true;
+ break;
+
+ case CSS_TYPE_COLOR:
+ if ((ttype == CSS_TK_COLOR ||
+ ttype == CSS_TK_SYMBOL) &&
+ (dStrcasecmp(tval, "rgb") == 0 ||
+ a_Color_parse(tval, -1, &err) != -1))
+ return true;
+ break;
+
+ case CSS_TYPE_STRING:
+ if (ttype == CSS_TK_STRING)
+ return true;
+ break;
+
+ case CSS_TYPE_SYMBOL:
+ if (ttype == CSS_TK_SYMBOL ||
+ ttype == CSS_TK_STRING)
+ return true;
+ break;
+
+ case CSS_TYPE_FONT_WEIGHT:
+ if (ttype == CSS_TK_DECINT) {
+ i = strtol(tval, NULL, 10);
+ if (i >= 100 && i <= 900)
+ return true;
+ }
+ break;
+
+ case CSS_TYPE_UNUSED:
+ case CSS_TYPE_INTEGER:
+ /* Not used for parser values. */
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+ *type = savedType;
+ return false;
+}
+
+bool CssParser::parseRgbColorComponent(int32_t *cc, int *percentage) {
+ if (ttype != CSS_TK_DECINT) {
+ MSG_CSS("expected integer not found in %s color\n", "rgb");
+ return false;
+ }
+
+ *cc = strtol(tval, NULL, 10);
+
+ nextToken();
+ if (ttype == CSS_TK_CHAR && tval[0] == '%') {
+ if (*percentage == 0) {
+ MSG_CSS("'%s' unexpected in rgb color\n", "%");
+ return false;
+ }
+ *percentage = 1;
+ *cc = *cc * 255 / 100;
+ nextToken();
+ } else {
+ if (*percentage == 1) {
+ MSG_CSS("expected '%s' not found in rgb color\n", "%");
+ return false;
+ }
+ *percentage = 0;
+ }
+
+ if (*cc > 255)
+ *cc = 255;
+ if (*cc < 0)
+ *cc = 0;
+
+ return true;
+}
+
+bool CssParser::parseRgbColor(int32_t *c) {
+ int32_t cc;
+ int percentage = -1;
+
+ *c = 0;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != '(') {
+ MSG_CSS("expected '%s' not found in rgb color\n", "(");
+ return false;
+ }
+ nextToken();
+
+ if (!parseRgbColorComponent(&cc, &percentage))
+ return false;
+ *c |= cc << 16;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ',') {
+ MSG_CSS("expected '%s' not found in rgb color\n", ",");
+ return false;
+ }
+ nextToken();
+
+ if (!parseRgbColorComponent(&cc, &percentage))
+ return false;
+ *c |= cc << 8;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ',') {
+ MSG_CSS("expected '%s' not found in rgb color\n", ",");
+ return false;
+ }
+ nextToken();
+
+ if (!parseRgbColorComponent(&cc, &percentage))
+ return false;
+ *c |= cc;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ')') {
+ MSG_CSS("expected '%s' not found in rgb color\n", ")");
+ return false;
+ }
+
+ return true;
+}
+
+bool CssParser::parseValue(CssPropertyName prop,
+ CssValueType type,
+ CssPropertyValue * val)
+{
+ CssLengthType lentype;
+ bool found, ret = false;
+ float fval;
+ int i, ival, err = 1;
+ Dstr *dstr;
+
+ switch (type) {
+ case CSS_TYPE_ENUM:
+ if (ttype == CSS_TK_SYMBOL) {
+ for (i = 0; Css_property_info[prop].enum_symbols[i]; i++)
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0) {
+ val->intVal = i;
+ ret = true;
+ break;
+ }
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_MULTI_ENUM:
+ val->intVal = 0;
+ ret = true;
+
+ while (ttype == CSS_TK_SYMBOL) {
+ if (dStrcasecmp(tval, "none") != 0) {
+ for (i = 0, found = false;
+ !found && Css_property_info[prop].enum_symbols[i]; i++) {
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0)
+ val->intVal |= (1 << i);
+ }
+ }
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_LENGTH_PERCENTAGE:
+ case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER:
+ case CSS_TYPE_LENGTH:
+ case CSS_TYPE_SIGNED_LENGTH:
+ if (ttype == CSS_TK_DECINT || ttype == CSS_TK_FLOAT) {
+ fval = atof(tval);
+ lentype = CSS_LENGTH_TYPE_NONE;
+
+ nextToken();
+ if (!spaceSeparated && ttype == CSS_TK_SYMBOL) {
+ ret = true;
+
+ if (dStrcasecmp(tval, "px") == 0) {
+ lentype = CSS_LENGTH_TYPE_PX;
+ nextToken();
+ } else if (dStrcasecmp(tval, "mm") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ nextToken();
+ } else if (dStrcasecmp(tval, "cm") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= 10;
+ nextToken();
+ } else if (dStrcasecmp(tval, "in") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= 25.4;
+ nextToken();
+ } else if (dStrcasecmp(tval, "pt") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= (25.4 / 72);
+ nextToken();
+ } else if (dStrcasecmp(tval, "pc") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= (25.4 / 6);
+ nextToken();
+ } else if (dStrcasecmp(tval, "em") == 0) {
+ lentype = CSS_LENGTH_TYPE_EM;
+ nextToken();
+ } else if (dStrcasecmp(tval, "ex") == 0) {
+ lentype = CSS_LENGTH_TYPE_EX;
+ nextToken();
+ } else {
+ ret = false;
+ }
+ } else if (!spaceSeparated &&
+ (type == CSS_TYPE_LENGTH_PERCENTAGE ||
+ type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER) &&
+ ttype == CSS_TK_CHAR &&
+ tval[0] == '%') {
+ fval /= 100;
+ lentype = CSS_LENGTH_TYPE_PERCENTAGE;
+ ret = true;
+ nextToken();
+ }
+
+ /* Allow numbers without unit only for 0 or
+ * CSS_TYPE_LENGTH_PERCENTAGE_NUMBER
+ */
+ if (lentype == CSS_LENGTH_TYPE_NONE &&
+ (type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER || fval == 0.0))
+ ret = true;
+
+ val->intVal = CSS_CREATE_LENGTH(fval, lentype);
+ } else if (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0) {
+ ret = true;
+ val->intVal = CSS_LENGTH_TYPE_AUTO;
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_COLOR:
+ if (ttype == CSS_TK_COLOR) {
+ val->intVal = a_Color_parse(tval, -1, &err);
+ if (err)
+ MSG_CSS("color is not in \"%s\" format\n", "#RRGGBB");
+ else
+ ret = true;
+ nextToken();
+ } else if (ttype == CSS_TK_SYMBOL) {
+ if (dStrcasecmp(tval, "rgb") == 0) {
+ nextToken();
+ if (parseRgbColor(&val->intVal))
+ ret = true;
+ else
+ MSG_CSS("Failed to parse %s color\n", "rgb(r,g,b)");
+ } else {
+ val->intVal = a_Color_parse(tval, -1, &err);
+ if (err)
+ MSG_CSS("color is not in \"%s\" format\n", "#RRGGBB");
+ else
+ ret = true;
+ }
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_STRING:
+ if (ttype == CSS_TK_STRING) {
+ val->strVal = dStrdup(tval);
+ ret = true;
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_SYMBOL:
+ /* Read comma separated list of font family names */
+ dstr = dStr_new("");
+ while (ttype == CSS_TK_SYMBOL || ttype == CSS_TK_STRING ||
+ (ttype == CSS_TK_CHAR && tval[0] == ',')) {
+ if (spaceSeparated)
+ dStr_append_c(dstr, ' ');
+ dStr_append(dstr, tval);
+ ret = true;
+ nextToken();
+ }
+
+ if (ret) {
+ val->strVal = dStrstrip(dstr->str);
+ dStr_free(dstr, 0);
+ } else {
+ dStr_free(dstr, 1);
+ }
+ break;
+
+ case CSS_TYPE_FONT_WEIGHT:
+ ival = 0;
+ if (ttype == CSS_TK_DECINT) {
+ ival = strtol(tval, NULL, 10);
+ if (ival < 100 || ival > 900)
+ /* invalid */
+ ival = 0;
+ }
+
+ if (ival != 0) {
+ val->intVal = ival;
+ ret = true;
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_UNUSED:
+ /* nothing */
+ break;
+
+ case CSS_TYPE_INTEGER:
+ /* Not used for parser values. */
+ default:
+ assert(false); /* not reached */
+ }
+
+ return ret;
+}
+
+bool CssParser::parseWeight()
+{
+ if (ttype == CSS_TK_CHAR && tval[0] == '!') {
+ nextToken();
+ if (ttype == CSS_TK_SYMBOL &&
+ dStrcasecmp(tval, "important") == 0) {
+ nextToken();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * bsearch(3) compare function for searching properties
+ */
+static int Css_property_info_cmp(const void *a, const void *b)
+{
+ return dStrcasecmp(((CssPropertyInfo *) a)->symbol,
+ ((CssPropertyInfo *) b)->symbol);
+}
+
+
+/*
+ * bsearch(3) compare function for searching shorthands
+ */
+static int Css_shorthand_info_cmp(const void *a, const void *b)
+{
+ return dStrcasecmp(((CssShorthandInfo *) a)->symbol,
+ ((CssShorthandInfo *) b)->symbol);
+}
+
+void CssParser::parseDeclaration(CssPropertyList * props,
+ CssPropertyList * importantProps)
+{
+ CssPropertyInfo pi = {NULL, {CSS_TYPE_UNUSED}, NULL}, *pip;
+ CssShorthandInfo si, *sip;
+ CssValueType type = CSS_TYPE_UNUSED;
+
+ CssPropertyName prop;
+ CssPropertyValue val, dir_vals[4];
+ CssValueType dir_types[4];
+ bool found, weight;
+ int sh_index, i, j, n;
+ int dir_set[4][4] = {
+ /* 1 value */ {0, 0, 0, 0},
+ /* 2 values */ {0, 0, 1, 1},
+ /* 3 values */ {0, 2, 1, 1},
+ /* 4 values */ {0, 2, 3, 1}
+ };
+
+ if (ttype == CSS_TK_SYMBOL) {
+ pi.symbol = tval;
+ pip =
+ (CssPropertyInfo *) bsearch(&pi, Css_property_info,
+ CSS_NUM_PARSED_PROPERTIES,
+ sizeof(CssPropertyInfo),
+ Css_property_info_cmp);
+ if (pip) {
+ prop = (CssPropertyName) (pip - Css_property_info);
+ nextToken();
+ if (ttype == CSS_TK_CHAR && tval[0] == ':') {
+ nextToken();
+ if (tokenMatchesProperty (prop, &type) &&
+ parseValue(prop, type, &val)) {
+ weight = parseWeight();
+ if (weight && importantProps)
+ importantProps->set(prop, type, val);
+ else
+ props->set(prop, type, val);
+ }
+ }
+ } else {
+ /* Try shorthands. */
+ si.symbol = tval;
+ sip =
+ (CssShorthandInfo *) bsearch(&pi, Css_shorthand_info,
+ CSS_SHORTHAND_NUM,
+ sizeof(CssShorthandInfo),
+ Css_shorthand_info_cmp);
+ if (sip) {
+ sh_index = sip - Css_shorthand_info;
+ nextToken();
+ if (ttype == CSS_TK_CHAR && tval[0] == ':') {
+ nextToken();
+
+ switch (Css_shorthand_info[sh_index].type) {
+
+ case CssShorthandInfo::CSS_SHORTHAND_FONT:
+ /* \todo Implement details. */
+ case CssShorthandInfo::CSS_SHORTHAND_MULTIPLE:
+ do {
+ for (found = false, i = 0;
+ !found &&
+ Css_shorthand_info[sh_index].properties[i] != -1;
+ i++)
+ if (tokenMatchesProperty(Css_shorthand_info[sh_index].
+ properties[i], &type)) {
+ found = true;
+ DEBUG_MSG(DEBUG_PARSE_LEVEL,
+ "will assign to '%s'\n",
+ Css_property_info
+ [Css_shorthand_info[sh_index]
+ .properties[i]].symbol);
+ if (parseValue(Css_shorthand_info[sh_index]
+ .properties[i], type, &val)) {
+ weight = parseWeight();
+ if (weight && importantProps)
+ importantProps->
+ set(Css_shorthand_info[sh_index].
+ properties[i], type, val);
+ else
+ props->set(Css_shorthand_info[sh_index].
+ properties[i], type, val);
+ }
+ }
+ } while (found);
+ break;
+
+ case CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS:
+ n = 0;
+ while (n < 4) {
+ if (tokenMatchesProperty(Css_shorthand_info[sh_index].
+ properties[0], &type) &&
+ parseValue(Css_shorthand_info[sh_index]
+ .properties[0], type, &val)) {
+ dir_vals[n] = val;
+ dir_types[n] = type;
+ n++;
+ } else
+ break;
+ }
+
+ weight = parseWeight();
+ if (n > 0) {
+ for (i = 0; i < 4; i++)
+ if (weight && importantProps)
+ importantProps->set(Css_shorthand_info[sh_index]
+ .properties[i],
+ dir_types[dir_set[n - 1][i]],
+ dir_vals[dir_set[n - 1][i]]);
+ else
+ props->set(Css_shorthand_info[sh_index]
+ .properties[i],
+ dir_types[dir_set[n - 1][i]],
+ dir_vals[dir_set[n - 1][i]]);
+ } else
+ MSG_CSS("no values for shorthand property '%s'\n",
+ Css_shorthand_info[sh_index].symbol);
+
+ break;
+
+ case CssShorthandInfo::CSS_SHORTHAND_BORDER:
+ do {
+ for (found = false, i = 0;
+ !found && i < 3;
+ i++)
+ if (tokenMatchesProperty(Css_shorthand_info[sh_index].
+ properties[i], &type)) {
+ found = true;
+ if (parseValue(Css_shorthand_info[sh_index]
+ .properties[i], type, &val)) {
+ weight = parseWeight();
+ for (j = 0; j < 4; j++)
+ if (weight && importantProps)
+ importantProps->
+ set(Css_shorthand_info[sh_index].
+ properties[j * 3 + i], type, val);
+ else
+ props->set(Css_shorthand_info[sh_index].
+ properties[j * 3 + i], type, val);
+ }
+ }
+ } while (found);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Skip all tokens until the expected end. */
+ while (!(ttype == CSS_TK_END ||
+ (ttype == CSS_TK_CHAR &&
+ (tval[0] == ';' || tval[0] == '}'))))
+ nextToken();
+
+ if (ttype == CSS_TK_CHAR && tval[0] == ';')
+ nextToken();
+}
+
+bool CssParser::parseSimpleSelector(CssSimpleSelector *selector)
+{
+ CssSimpleSelector::SelectType selectType;
+
+ if (ttype == CSS_TK_SYMBOL) {
+ selector->setElement (a_Html_tag_index(tval));
+ nextToken();
+ if (spaceSeparated)
+ return true;
+ } else if (ttype == CSS_TK_CHAR && tval[0] == '*') {
+ selector->setElement (CssSimpleSelector::ELEMENT_ANY);
+ nextToken();
+ if (spaceSeparated)
+ return true;
+ } else if (ttype == CSS_TK_CHAR &&
+ (tval[0] == '#' ||
+ tval[0] == '.' ||
+ tval[0] == ':')) {
+ // nothing to be done in this case
+ } else {
+ return false;
+ }
+
+ do {
+ selectType = CssSimpleSelector::SELECT_NONE;
+ if (ttype == CSS_TK_CHAR) {
+ switch (tval[0]) {
+ case '#':
+ selectType = CssSimpleSelector::SELECT_ID;
+ break;
+ case '.':
+ selectType = CssSimpleSelector::SELECT_CLASS;
+ break;
+ case ':':
+ selectType = CssSimpleSelector::SELECT_PSEUDO_CLASS;
+ if (selector->getPseudoClass ())
+ // pseudo class has been set already.
+ // As dillo currently only supports :link and :visisted, a
+ // selector with more than one pseudo class will never match.
+ // By returning false, the whole CssRule will be dropped.
+ // \todo adapt this when supporting :hover, :active...
+ return false;
+ break;
+ }
+ }
+
+ if (selectType != CssSimpleSelector::SELECT_NONE) {
+ nextToken();
+ if (spaceSeparated)
+ return true;
+
+ if (ttype == CSS_TK_SYMBOL) {
+ selector->setSelect (selectType, tval);
+ nextToken();
+ } else {
+ return false; // don't accept classes or id's starting with integer
+ }
+ if (spaceSeparated)
+ return true;
+ }
+ } while (selectType != CssSimpleSelector::SELECT_NONE);
+
+ DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of simple selector (%s, %s, %s, %d)\n",
+ selector->id, selector->klass,
+ selector->pseudo, selector->element);
+
+ return true;
+}
+
+CssSelector *CssParser::parseSelector()
+{
+ CssSelector *selector = new CssSelector ();
+
+ while (true) {
+ if (! parseSimpleSelector (selector->top ())) {
+ delete selector;
+ selector = NULL;
+ break;
+ }
+
+ if (ttype == CSS_TK_CHAR &&
+ (tval[0] == ',' || tval[0] == '{')) {
+ break;
+ } else if (ttype == CSS_TK_CHAR && tval[0] == '>') {
+ selector->addSimpleSelector (CssSelector::CHILD);
+ nextToken();
+ } else if (ttype != CSS_TK_END && spaceSeparated) {
+ selector->addSimpleSelector (CssSelector::DESCENDANT);
+ } else {
+ delete selector;
+ selector = NULL;
+ break;
+ }
+ }
+
+ while (ttype != CSS_TK_END &&
+ (ttype != CSS_TK_CHAR ||
+ (tval[0] != ',' && tval[0] != '{')))
+ nextToken();
+
+ return selector;
+}
+
+void CssParser::parseRuleset()
+{
+ lout::misc::SimpleVector < CssSelector * >*list;
+ CssPropertyList *props, *importantProps;
+ CssSelector *selector;
+
+ list = new lout::misc::SimpleVector < CssSelector * >(1);
+
+ while (true) {
+ selector = parseSelector();
+
+ if (selector) {
+ selector->ref();
+ list->increase();
+ list->set(list->size() - 1, selector);
+ }
+
+ // \todo dump whole ruleset in case of parse error as required by CSS 2.1
+ // however make sure we don't dump it if only dillo fails to parse
+ // valid CSS.
+
+ if (ttype == CSS_TK_CHAR && tval[0] == ',')
+ /* To read the next token. */
+ nextToken();
+ else
+ /* No more selectors. */
+ break;
+ }
+
+ DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of %s\n", "selectors");
+
+ props = new CssPropertyList(true);
+ props->ref();
+ importantProps = new CssPropertyList(true);
+ importantProps->ref();
+
+ /* Read block. ('{' has already been read.) */
+ if (ttype != CSS_TK_END) {
+ withinBlock = true;
+ nextToken();
+ do
+ parseDeclaration(props, importantProps);
+ while (!(ttype == CSS_TK_END ||
+ (ttype == CSS_TK_CHAR && tval[0] == '}')));
+ withinBlock = false;
+ }
+
+ for (int i = 0; i < list->size(); i++) {
+ CssSelector *s = list->get(i);
+
+ if (origin == CSS_ORIGIN_USER_AGENT) {
+ context->addRule(s, props, CSS_PRIMARY_USER_AGENT);
+ } else if (origin == CSS_ORIGIN_USER) {
+ context->addRule(s, props, CSS_PRIMARY_USER);
+ context->addRule(s, importantProps, CSS_PRIMARY_USER_IMPORTANT);
+ } else if (origin == CSS_ORIGIN_AUTHOR) {
+ context->addRule(s, props, CSS_PRIMARY_AUTHOR);
+ context->addRule(s, importantProps, CSS_PRIMARY_AUTHOR_IMPORTANT);
+ }
+
+ s->unref();
+ }
+
+ props->unref();
+ importantProps->unref();
+
+ delete list;
+
+ if (ttype == CSS_TK_CHAR && tval[0] == '}')
+ nextToken();
+}
+
+char * CssParser::parseUrl()
+{
+ Dstr *urlStr = NULL;
+
+ if (ttype != CSS_TK_SYMBOL ||
+ dStrcasecmp(tval, "url") != 0)
+ return NULL;
+
+ nextToken();
+
+ if (ttype != CSS_TK_CHAR || tval[0] != '(')
+ return NULL;
+
+ nextToken();
+
+ if (ttype == CSS_TK_STRING) {
+ urlStr = dStr_new(tval);
+ nextToken();
+ } else {
+ urlStr = dStr_new("");
+ while (ttype != CSS_TK_END &&
+ (ttype != CSS_TK_CHAR || tval[0] != ')')) {
+ dStr_append(urlStr, tval);
+ nextToken();
+ }
+ }
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ')') {
+ dStr_free(urlStr, 1);
+ urlStr = NULL;
+ }
+
+ if (urlStr) {
+ char *url = urlStr->str;
+ dStr_free(urlStr, 0);
+ return url;
+ } else {
+ return NULL;
+ }
+}
+
+void CssParser::parseImport(DilloHtml *html, DilloUrl *baseUrl)
+{
+ char *urlStr = NULL;
+
+ if (html != NULL &&
+ ttype == CSS_TK_SYMBOL &&
+ dStrcasecmp(tval, "import") == 0) {
+ nextToken();
+
+ if (ttype == CSS_TK_SYMBOL &&
+ dStrcasecmp(tval, "url") == 0)
+ urlStr = parseUrl();
+ else if (ttype == CSS_TK_STRING)
+ urlStr = dStrdup (tval);
+
+ /* Skip all tokens until the expected end. */
+ while (!(ttype == CSS_TK_END ||
+ (ttype == CSS_TK_CHAR && (tval[0] == ';'))))
+ nextToken();
+
+ nextToken();
+
+ if (urlStr) {
+ MSG("CssParser::parseImport(): @import %s\n", urlStr);
+ DilloUrl *url = a_Html_url_new (html, urlStr, a_Url_str(baseUrl),
+ baseUrl ? 1 : 0);
+ a_Html_load_stylesheet(html, url);
+ a_Url_free(url);
+ dFree (urlStr);
+ }
+ }
+}
+
+const char * CssParser::propertyNameString(CssPropertyName name)
+{
+ return Css_property_info[name].symbol;
+}
+
+void CssParser::parse(DilloHtml *html, DilloUrl *url, CssContext * context,
+ const char *buf,
+ int buflen, CssOrigin origin)
+{
+ CssParser parser (context, origin, buf, buflen);
+
+ while (parser.ttype == CSS_TK_CHAR && parser.tval[0] == '@') {
+ parser.nextToken();
+ parser.parseImport(html, url);
+ }
+
+ while (parser.ttype != CSS_TK_END)
+ parser.parseRuleset();
+}
+
+CssPropertyList *CssParser::parseDeclarationBlock(const char *buf, int buflen)
+{
+ CssPropertyList *props = new CssPropertyList (true);
+ CssParser parser (NULL, CSS_ORIGIN_AUTHOR, buf, buflen);
+
+ parser.withinBlock = true;
+
+ do
+ parser.parseDeclaration(props, NULL);
+ while (!(parser.ttype == CSS_TK_END ||
+ (parser.ttype == CSS_TK_CHAR && parser.tval[0] == '}')));
+
+ if (props->size () == 0) {
+ delete props;
+ props = NULL;
+ }
+
+ return props;
+}
diff --git a/src/cssparser.hh b/src/cssparser.hh
new file mode 100644
index 00000000..1e471c68
--- /dev/null
+++ b/src/cssparser.hh
@@ -0,0 +1,54 @@
+#ifndef __CSSPARSER_HH__
+#define __CSSPARSER_HH__
+
+#include "css.hh"
+#include "html_common.hh"
+
+class CssParser {
+ private:
+ typedef enum {
+ CSS_TK_DECINT, CSS_TK_FLOAT, CSS_TK_COLOR, CSS_TK_SYMBOL,
+ CSS_TK_STRING, CSS_TK_CHAR, CSS_TK_END
+ } CssTokenType;
+
+ static const int maxStrLen = 256;
+ CssContext *context;
+ CssOrigin origin;
+
+ const char *buf;
+ int buflen, bufptr;
+
+ CssTokenType ttype;
+ char tval[maxStrLen];
+ bool withinBlock;
+ bool spaceSeparated; /* used when parsing CSS selectors */
+
+ CssParser(CssContext *context, CssOrigin origin,
+ const char *buf, int buflen);
+ int getChar();
+ void ungetChar();
+ void nextToken();
+ bool skipString(int c, const char *string);
+ bool tokenMatchesProperty(CssPropertyName prop, CssValueType * type);
+ bool parseValue(CssPropertyName prop, CssValueType type,
+ CssPropertyValue * val);
+ bool parseWeight();
+ bool parseRgbColorComponent(int32_t *cc, int *percentage);
+ bool parseRgbColor(int32_t *c);
+ void parseDeclaration(CssPropertyList * props,
+ CssPropertyList * importantProps);
+ bool parseSimpleSelector(CssSimpleSelector *selector);
+ char *parseUrl();
+ void parseImport(DilloHtml *html, DilloUrl *url);
+ CssSelector *parseSelector();
+ void parseRuleset();
+
+ public:
+ static CssPropertyList *parseDeclarationBlock(const char *buf,
+ int buflen);
+ static void parse(DilloHtml *html, DilloUrl *url, CssContext *context,
+ const char *buf, int buflen, CssOrigin origin);
+ static const char *propertyNameString(CssPropertyName name);
+};
+
+#endif
diff --git a/src/decode.c b/src/decode.c
index ff4fcb27..24067318 100644
--- a/src/decode.c
+++ b/src/decode.c
@@ -15,6 +15,7 @@
#include <stdlib.h> /* strtol */
#include "decode.h"
+#include "utf8.hh"
#include "msg.h"
static const int bufsize = 8*1024;
@@ -106,13 +107,12 @@ static Dstr *Decode_gzip(Decode *dc, const char *instr, int inlen)
rc = inflate(zs, Z_SYNC_FLUSH);
+ dStr_append_l(output, dc->buffer, zs->total_out);
+
if ((rc == Z_OK) || (rc == Z_STREAM_END)) {
// Z_STREAM_END at end of file
inputConsumed += zs->total_in;
-
- dStr_append_l(output, dc->buffer, zs->total_out);
-
zs->total_out = 0;
zs->total_in = 0;
} else if (rc == Z_DATA_ERROR) {
@@ -126,6 +126,7 @@ static void Decode_gzip_free(Decode *dc)
{
(void)inflateEnd((z_stream *)dc->state);
+ dFree(dc->state);
dFree(dc->buffer);
}
@@ -164,15 +165,8 @@ static Dstr *Decode_charset(Decode *dc, const char *instr, int inlen)
if (rc == EILSEQ){
inPtr++;
inLeft--;
- /*
- * U+FFFD: "used to replace an incoming character whose value is
- * unknown or unrepresentable in Unicode."
- */
- //dStr_append(output, "\ufffd");
- // \uxxxx is C99. UTF-8-specific:
- dStr_append_c(output, 0xEF);
- dStr_append_c(output, 0xBF);
- dStr_append_c(output, 0xBD);
+ dStr_append_l(output, utf8_replacement_char,
+ sizeof(utf8_replacement_char) - 1);
}
}
dStr_erase(dc->leftover, 0, dc->leftover->len - inLeft);
@@ -182,6 +176,7 @@ static Dstr *Decode_charset(Decode *dc, const char *instr, int inlen)
static void Decode_charset_free(Decode *dc)
{
+ /* iconv_close() frees dc->state */
(void)iconv_close((iconv_t)(dc->state));
dFree(dc->buffer);
@@ -195,7 +190,7 @@ Decode *a_Decode_transfer_init(const char *format)
{
Decode *dc = NULL;
- if (format && !dStrncasecmp(format, "chunked", 7)) {
+ if (format && !dStrcasecmp(format, "chunked")) {
int *chunk_remaining = dNew(int, 1);
*chunk_remaining = 0;
dc = dNew(Decode, 1);
@@ -221,9 +216,9 @@ Decode *a_Decode_content_init(const char *format)
if (format && *format) {
if (!dStrcasecmp(format, "gzip") || !dStrcasecmp(format, "x-gzip")) {
+ z_stream *zs;
_MSG("gzipped data!\n");
- z_stream *zs;
dc = dNew(Decode, 1);
dc->buffer = dNew(char, bufsize);
dc->state = zs = dNew(z_stream, 1);
@@ -242,7 +237,7 @@ Decode *a_Decode_content_init(const char *format)
MSG("Content-Encoding '%s' not recognized.\n", format);
}
}
- return dc;
+ return dc;
}
/*
@@ -289,10 +284,10 @@ Decode *a_Decode_charset_init(const char *format)
dc->decode = Decode_charset;
dc->free = Decode_charset_free;
} else {
- MSG("Unable to convert from character encoding: '%s'\n", format);
+ MSG_WARN("Unable to convert from character encoding: '%s'\n", format);
}
}
- return dc;
+ return dc;
}
/*
diff --git a/src/dgif.h b/src/dgif.h
new file mode 100644
index 00000000..ec36812d
--- /dev/null
+++ b/src/dgif.h
@@ -0,0 +1,19 @@
+#ifndef __GIF_H__
+#define __GIF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "url.h"
+#include "image.hh"
+
+
+void *a_Gif_new(DilloImage *Image, DilloUrl *url, int version);
+void a_Gif_callback(int Op, void *data);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* !__GIF_H__ */
diff --git a/src/dialog.cc b/src/dialog.cc
index 69781f32..47af9921 100644
--- a/src/dialog.cc
+++ b/src/dialog.cc
@@ -11,7 +11,7 @@
// UI dialogs
-#include <math.h> // for rint()
+#include <math.h> // for rint()
#include <fltk/Window.h>
#include <fltk/ask.h>
@@ -20,6 +20,9 @@
#include <fltk/ReturnButton.h>
#include <fltk/TextDisplay.h>
#include <fltk/HighlightButton.h>
+#include <fltk/WordwrapOutput.h>
+#include <fltk/Input.h>
+#include <fltk/SecretInput.h>
#include "msg.h"
#include "dialog.hh"
@@ -118,19 +121,19 @@ char *a_Dialog_open_file(const char *msg,
}
/*
- * Make a new window with the provided text
+ * Show a new window with the provided text
*/
-void *a_Dialog_make_text_window(const char *txt, const char *title)
+void a_Dialog_text_window(const char *txt, const char *title)
{
//int wh = 600, ww = 650, bh = 30;
int wh = prefs.height, ww = prefs.width, bh = 30;
int lines, line_num_width;
- Font *textfont = font(prefs.fw_fontname, 0);
-
+ Font *textfont = font(prefs.font_monospace, 0);
+
Window *window = new Window(ww, wh, title ? title : "Untitled");
window->callback(window_close_cb, window);
window->begin();
-
+
TextDisplay *td = new TextDisplay(0,0,ww, wh-bh);
td->buffer()->text(txt);
@@ -156,15 +159,7 @@ void *a_Dialog_make_text_window(const char *txt, const char *title)
window->resizable(td);
window->end();
- return window;
-}
-
-/*
- * Show a window.
- */
-void a_Dialog_show_text_window(void *vWindow)
-{
- ((Window *)vWindow)->show();
+ window->show();
}
/*--------------------------------------------------------------------------*/
@@ -193,7 +188,7 @@ int a_Dialog_choice5(const char *QuestionTxt,
txt[0] = txt[6] = NULL;
txt[1] = alt1; txt[2] = alt2; txt[3] = alt3;
txt[4] = alt4; txt[5] = alt5;
- for (int i=1; txt[i]; ++i, ++nb);
+ for (int i=1; txt[i]; ++i, ++nb) ;
Window *window = new Window(ww,wh,"Choice5");
window->begin();
@@ -214,7 +209,7 @@ int a_Dialog_choice5(const char *QuestionTxt,
b = new HighlightButton(xpos, wh-bh, bw, bh, txt[i]);
b->align(ALIGN_WRAP|ALIGN_CLIP);
b->box(UP_BOX);
- b->callback(choice5_cb, (void*)i);
+ b->callback(choice5_cb, INT2VOIDP(i));
xpos += bw + gap;
}
window->end();
@@ -227,3 +222,81 @@ int a_Dialog_choice5(const char *QuestionTxt,
return choice5_answer;
}
+
+/*--------------------------------------------------------------------------*/
+/*
+ * ret: 0 = Cancel, 1 = OK
+ */
+static void Dialog_user_password_cb(Widget *button, void *vIntPtr)
+{
+ int ret = VOIDP2INT(vIntPtr);
+ _MSG("Dialog_user_password_cb: %d\n", ret);
+ button->window()->make_exec_return(ret);
+}
+
+/*
+ * Make a user/password dialog.
+ * Call the callback with the result (OK or not) and the given user and
+ * password if OK.
+ */
+int a_Dialog_user_password(const char *message, UserPasswordCB cb, void *vp)
+{
+ int ok,
+ window_w = 300, window_h = 280,
+ input_x = 80, input_w = 200, input_h = 30,
+ button_y = 230, button_h = 30;
+
+ Window *window =
+ new Window(window_w,window_h,"User/Password");
+ window->begin();
+
+ /* message */
+ WordwrapOutput *message_output =
+ new WordwrapOutput(20,20,window_w-40,100);
+ message_output->box(DOWN_BOX);
+ message_output->text(message);
+ message_output->textfont(HELVETICA_BOLD_ITALIC);
+ message_output->textsize(14);
+
+ /* inputs */
+ Input *user_input =
+ new Input(input_x,140,input_w,input_h,"User");
+ user_input->labelsize(14);
+ user_input->textsize(14);
+ SecretInput *password_input =
+ new SecretInput(input_x,180,input_w,input_h,"Password");
+ password_input->labelsize(14);
+ password_input->textsize(14);
+
+ /* "OK" button */
+ Button *ok_button =
+ new Button(200,button_y,50,button_h,"OK");
+ ok_button->labelsize(14);
+ ok_button->callback(Dialog_user_password_cb);
+ ok_button->user_data(INT2VOIDP(1));
+
+ /* "Cancel" button */
+ Button *cancel_button =
+ new Button(50,button_y,100,button_h,"Cancel");
+ cancel_button->labelsize(14);
+ cancel_button->callback(Dialog_user_password_cb);
+ cancel_button->user_data(INT2VOIDP(0));
+
+ window->end();
+ window->size_range(window_w,window_h,window_w,window_h);
+ window->resizable(window);
+
+ if ((ok = window->exec())) {
+ /* call the callback */
+ const char *user, *password;
+ user = user_input->value();
+ password = password_input->value();
+ _MSG("a_Dialog_user_passwd: ok = %d\n", ok);
+ (*cb)(user, password, vp);
+ }
+
+ delete window;
+
+ return ok;
+}
+
diff --git a/src/dialog.hh b/src/dialog.hh
index 17c326cb..440e9bba 100644
--- a/src/dialog.hh
+++ b/src/dialog.hh
@@ -5,12 +5,16 @@
extern "C" {
#endif /* __cplusplus */
+typedef void (*UserPasswordCB)(const char *user, const char *password,
+ void *vp);
+
void a_Dialog_msg(const char *msg);
int a_Dialog_choice3(const char *msg,
const char *b0, const char *b1, const char *b2);
int a_Dialog_choice5(const char *QuestionTxt,
const char *alt1, const char *alt2, const char *alt3,
const char *alt4, const char *alt5);
+int a_Dialog_user_password(const char *message, UserPasswordCB cb, void *vp);
const char *a_Dialog_input(const char *msg);
const char *a_Dialog_passwd(const char *msg);
const char *a_Dialog_save_file(const char *msg,
@@ -19,8 +23,7 @@ const char *a_Dialog_select_file(const char *msg,
const char *pattern, const char *fname);
char *a_Dialog_open_file(const char *msg,
const char *pattern, const char *fname);
-void *a_Dialog_make_text_window(const char *txt, const char *title);
-void a_Dialog_show_text_window(void *vWindow);
+void a_Dialog_text_window(const char *txt, const char *title);
#ifdef __cplusplus
}
diff --git a/src/dicache.c b/src/dicache.c
index 97ed1915..e700f000 100644
--- a/src/dicache.c
+++ b/src/dicache.c
@@ -9,18 +9,26 @@
* (at your option) any later version.
*/
-#include <sys/time.h> /* for libc5 compatibility */
#include <string.h> /* for memset */
-#include <stdio.h>
#include <stdlib.h>
+#include "msg.h"
#include "image.hh"
+#include "imgbuf.hh"
#include "web.hh"
#include "dicache.h"
-#include "cache.h"
+#include "dpng.h"
+#include "dgif.h"
+#include "djpeg.h"
typedef struct _DICacheNode DICacheNode;
+enum {
+ DIC_Gif,
+ DIC_Png,
+ DIC_Jpeg
+};
+
struct _DICacheNode {
int valid; /* flag */
DilloUrl *url; /* primary "Key" for this dicache entry */
@@ -33,9 +41,9 @@ struct _DICacheNode {
*/
static Dlist *CachedIMGs = NULL;
-static int dicache_size_total; /* invariant: dicache_size_total is
- * the sum of the image sizes (3*w*h)
- * of all the images in the dicache. */
+static uint_t dicache_size_total; /* invariant: dicache_size_total is
+ * the sum of the image sizes (3*w*h)
+ * of all the images in the dicache. */
/*
* Compare two dicache nodes
@@ -78,15 +86,18 @@ static DICacheEntry *Dicache_entry_new(void)
entry->height = 0;
entry->type = DILLO_IMG_TYPE_NOTSET;
entry->cmap = NULL;
- entry->linebuf = NULL;
entry->v_imgbuf = NULL;
entry->RefCount = 1;
entry->TotalSize = 0;
- entry->Y = 0;
entry->ScanNumber = 0;
entry->BitVec = NULL;
entry->State = DIC_Empty;
- entry->version = 0;
+ entry->version = 1;
+
+ entry->Decoder = NULL;
+ entry->DecoderData = NULL;
+ entry->DecodedSize = 0;
+
entry->next = NULL;
return entry;
@@ -96,7 +107,7 @@ static DICacheEntry *Dicache_entry_new(void)
* Add a new entry in the dicache
* (a single node (URL) may have several entries)
*/
-DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url)
+static DICacheEntry *Dicache_add_entry(const DilloUrl *Url)
{
DICacheEntry *entry;
DICacheNode *node;
@@ -127,41 +138,31 @@ DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url)
}
/*
- * Search an entry in the dicache (given the Url).
- * Return value: a pointer to the entry of the _newest_ (i.e. highest)
- * version if found; NULL otherwise.
- */
-DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url)
-{
- DICacheNode *node;
- DICacheEntry *entry;
-
- node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
-
- if (!node || !node->valid)
- return NULL;
-
- for (entry = node->first; (entry && entry->next); entry = entry->next);
-
- return entry;
-}
-
-/*
* Search a particular version of a URL in the Dicache.
* Return value: a pointer to the entry if found; NULL otherwise.
+ *
+ * Notes: DIC_Last means last version of the image.
+ * version zero is not allowed.
*/
-static DICacheEntry *Dicache_get_entry_version(const DilloUrl *Url,
- int version)
+DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url, int version)
{
DICacheNode *node;
- DICacheEntry *entry;
+ DICacheEntry *entry = NULL;
- node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
- entry = (node) ? node->first : NULL;
-
- while (entry && entry->version != version)
- entry = entry->next;
+ dReturn_val_if_fail(version != 0, NULL);
+ node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
+ if (node) {
+ if (version == DIC_Last) {
+ if (node->valid) {
+ entry = node->first;
+ for ( ; (entry && entry->next); entry = entry->next);
+ }
+ } else {
+ entry = node->first;
+ for ( ; entry && entry->version != version; entry = entry->next) ;
+ }
+ }
return entry;
}
@@ -172,7 +173,7 @@ static void Dicache_remove(const DilloUrl *Url, int version)
{
DICacheNode *node;
DICacheEntry *entry, *prev;
-
+ _MSG("Dicache_remove url=%s\n", URL_STR(Url));
node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
prev = entry = (node) ? node->first : NULL;
@@ -182,11 +183,15 @@ static void Dicache_remove(const DilloUrl *Url, int version)
}
if (entry) {
+ _MSG("Dicache_remove Decoder=%p DecoderData=%p\n",
+ entry->Decoder, entry->DecoderData);
/* Eliminate this dicache entry */
dFree(entry->cmap);
- dFree(entry->linebuf);
a_Bitvec_free(entry->BitVec);
- a_Image_imgbuf_unref(entry->v_imgbuf);
+ a_Imgbuf_unref(entry->v_imgbuf);
+ if (entry->Decoder) {
+ entry->Decoder(CA_Abort, entry->DecoderData);
+ }
dicache_size_total -= entry->TotalSize;
if (node->first == entry) {
@@ -206,14 +211,14 @@ static void Dicache_remove(const DilloUrl *Url, int version)
/*
* Unrefs the counter of a dicache entry, and _if_ no DwImage is acessing
- * this buffer, then we call Dicache_free to do the dirty job.
+ * this buffer, then we call Dicache_remove() to do the job.
*/
void a_Dicache_unref(const DilloUrl *Url, int version)
{
DICacheEntry *entry;
- if ((entry = Dicache_get_entry_version(Url, version))) {
- /*if (--entry->RefCount == 0 && (entry->next || !prefs.use_dicache)) {*/
+ _MSG("a_Dicache_unref\n");
+ if ((entry = a_Dicache_get_entry(Url, version))) {
if (--entry->RefCount == 0) {
Dicache_remove(Url, version);
}
@@ -223,12 +228,11 @@ void a_Dicache_unref(const DilloUrl *Url, int version)
/*
* Refs the counter of a dicache entry.
*/
-
DICacheEntry* a_Dicache_ref(const DilloUrl *Url, int version)
{
DICacheEntry *entry;
- if ((entry = Dicache_get_entry_version(Url, version))) {
+ if ((entry = a_Dicache_get_entry(Url, version))) {
++entry->RefCount;
}
return entry;
@@ -236,7 +240,8 @@ DICacheEntry* a_Dicache_ref(const DilloUrl *Url, int version)
/*
* Invalidate this entry. This is used for the reloading mechanism.
- * Can't erase current versions, but a_Dicache_get_entry must return NULL.
+ * Can't erase current versions, but a_Dicache_get_entry(url, DIC_Last)
+ * must return NULL.
*/
void a_Dicache_invalidate_entry(const DilloUrl *Url)
{
@@ -251,57 +256,6 @@ void a_Dicache_invalidate_entry(const DilloUrl *Url)
/* ------------------------------------------------------------------------- */
/*
- * This function is a cache client; (but feeds its clients from dicache)
- */
-void a_Dicache_callback(int Op, CacheClient_t *Client)
-{
- /* TODO: Handle Op = CA_Abort (to show what was got) --Jcid */
- uint_t i;
- DilloWeb *Web = Client->Web;
- DilloImage *Image = Web->Image;
- DICacheEntry *DicEntry = a_Dicache_get_entry(Web->url);
-
- dReturn_if_fail ( DicEntry != NULL );
-
- /* when the data stream is not an image 'v_imgbuf' remains NULL */
- if (Op == CA_Send && DicEntry->v_imgbuf) {
- if (Image->height == 0 && DicEntry->State >= DIC_SetParms) {
- /* Set parms */
- a_Image_set_parms(
- Image, DicEntry->v_imgbuf, DicEntry->url,
- DicEntry->version, DicEntry->width, DicEntry->height,
- DicEntry->type);
- }
- if (DicEntry->State == DIC_Write) {
- if (DicEntry->ScanNumber == Image->ScanNumber) {
- for (i = 0; i < DicEntry->height; ++i)
- if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) &&
- !a_Bitvec_get_bit(Image->BitVec, (int)i) )
- a_Image_write(Image, DicEntry->v_imgbuf,
- DicEntry->linebuf, i, FALSE);
- } else {
- for (i = 0; i < DicEntry->height; ++i) {
- if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) ||
- !a_Bitvec_get_bit(Image->BitVec, (int)i) ||
- DicEntry->ScanNumber > Image->ScanNumber + 1) {
- a_Image_write(Image, DicEntry->v_imgbuf,
- DicEntry->linebuf, i, FALSE);
- }
- if (!a_Bitvec_get_bit(DicEntry->BitVec, (int)i))
- a_Bitvec_clear_bit(Image->BitVec, (int)i);
- }
- Image->ScanNumber = DicEntry->ScanNumber;
- }
- }
- } else if (Op == CA_Close || Op == CA_Abort) {
- a_Image_close(Web->Image);
- a_Bw_close_client(Web->bw, Client->Key);
- }
-}
-
-/* ------------------------------------------------------------------------- */
-
-/*
* Set image's width, height & type
* (By now, we'll use the image information despite the html tags --Jcid)
*/
@@ -309,37 +263,29 @@ void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
uint_t width, uint_t height, DilloImgType type)
{
DICacheEntry *DicEntry;
- size_t Size = width * height * 3;
+ _MSG("a_Dicache_set_parms (%s)\n", URL_STR(url));
dReturn_if_fail ( Image != NULL && width && height );
/* Find the DicEntry for this Image */
- DicEntry = Dicache_get_entry_version(url, version);
+ DicEntry = a_Dicache_get_entry(url, version);
dReturn_if_fail ( DicEntry != NULL );
+ /* Parameters already set? */
+ dReturn_if_fail ( DicEntry->State < DIC_SetParms );
- /* Initialize the DicEntry */
- DicEntry->linebuf = dNew(uchar_t, width * 3);
- dReturn_if_fail ( DicEntry->linebuf != NULL );
+ _MSG(" RefCount=%d version=%d\n", DicEntry->RefCount, DicEntry->version);
/* BUG: there's just one image-type now */
#define I_RGB 0
- DicEntry->v_imgbuf = a_Image_imgbuf_new(Image->dw, I_RGB, width, height);
+ DicEntry->v_imgbuf = a_Imgbuf_new(Image->dw, I_RGB, width, height);
- /* This extra reference activates the dicache ALWAYS.
- * Extra code is necessary in Imgbuf to be able to free it */
- //a_Image_imgbuf_ref(DicEntry->v_imgbuf);
-
- DicEntry->TotalSize = Size;
+ DicEntry->TotalSize = width * height * 3;
DicEntry->width = width;
DicEntry->height = height;
DicEntry->type = type;
DicEntry->BitVec = a_Bitvec_new((int)height);
DicEntry->State = DIC_SetParms;
- dicache_size_total += Size;
-
- /* Allocate and initialize this image */
- a_Image_set_parms(Image, DicEntry->v_imgbuf, url, version,
- width, height, type);
+ dicache_size_total += DicEntry->TotalSize;
}
/*
@@ -349,8 +295,9 @@ void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
const uchar_t *cmap, uint_t num_colors,
int num_colors_max, int bg_index)
{
- DICacheEntry *DicEntry = Dicache_get_entry_version(url, version);
+ DICacheEntry *DicEntry = a_Dicache_get_entry(url, version);
+ _MSG("a_Dicache_set_cmap\n");
dReturn_if_fail ( DicEntry != NULL );
dFree(DicEntry->cmap);
@@ -362,24 +309,27 @@ void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
DicEntry->cmap[bg_index * 3 + 2] = (Image->bg_color) & 0xff;
}
- a_Image_set_cmap(Image, DicEntry->cmap);
DicEntry->State = DIC_SetCmap;
}
/*
* Reset for a new scan from a multiple-scan image.
*/
-void a_Dicache_new_scan(DilloImage *image, const DilloUrl *url, int version)
+void a_Dicache_new_scan(const DilloUrl *url, int version)
{
DICacheEntry *DicEntry;
+ _MSG("a_Dicache_new_scan\n");
dReturn_if_fail ( url != NULL );
- DicEntry = Dicache_get_entry_version(url, version);
+ DicEntry = a_Dicache_get_entry(url, version);
dReturn_if_fail ( DicEntry != NULL );
-
+ if (DicEntry->State < DIC_SetParms) {
+ MSG("a_Dicache_new_scan before DIC_SetParms\n");
+ exit(1);
+ }
a_Bitvec_clear(DicEntry->BitVec);
DicEntry->ScanNumber++;
- a_Image_new_scan(image, DicEntry->v_imgbuf);
+ a_Imgbuf_new_scan(DicEntry->v_imgbuf);
}
/*
@@ -388,18 +338,19 @@ void a_Dicache_new_scan(DilloImage *image, const DilloUrl *url, int version)
* buf: row buffer
* Y : row number
*/
-void a_Dicache_write(DilloImage *Image, DilloUrl *url, int version,
- const uchar_t *buf, uint_t Y)
+void a_Dicache_write(DilloUrl *url, int version, const uchar_t *buf, uint_t Y)
{
DICacheEntry *DicEntry;
- dReturn_if_fail ( Image != NULL );
- DicEntry = Dicache_get_entry_version(url, version);
+ _MSG("a_Dicache_write\n");
+ DicEntry = a_Dicache_get_entry(url, version);
dReturn_if_fail ( DicEntry != NULL );
dReturn_if_fail ( DicEntry->width > 0 && DicEntry->height > 0 );
- a_Image_write(Image, DicEntry->v_imgbuf, buf, Y, TRUE);
- DicEntry->Y = Y;
+ /* update the common buffer in the imgbuf */
+ a_Imgbuf_update(DicEntry->v_imgbuf, buf, DicEntry->type,
+ DicEntry->cmap, DicEntry->width, DicEntry->height, Y);
+
a_Bitvec_set_bit(DicEntry->BitVec, (int)Y);
DicEntry->State = DIC_Write;
}
@@ -410,19 +361,169 @@ void a_Dicache_write(DilloImage *Image, DilloUrl *url, int version,
void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client)
{
DilloWeb *Web = Client->Web;
- DICacheEntry *DicEntry = Dicache_get_entry_version(url, version);
+ DICacheEntry *DicEntry = a_Dicache_get_entry(url, version);
dReturn_if_fail ( DicEntry != NULL );
- DicEntry->State = DIC_Close;
- dFree(DicEntry->cmap);
- DicEntry->cmap = NULL;
- dFree(DicEntry->linebuf);
- DicEntry->linebuf = NULL;
- a_Image_close(Web->Image);
+ /* a_Dicache_unref() may free DicEntry */
+ _MSG("a_Dicache_close RefCount=%d\n", DicEntry->RefCount - 1);
+
+ if (DicEntry->State < DIC_Close) {
+ DicEntry->State = DIC_Close;
+ dFree(DicEntry->cmap);
+ DicEntry->cmap = NULL;
+ DicEntry->Decoder = NULL;
+ DicEntry->DecoderData = NULL;
+ }
+ a_Dicache_unref(url, version);
+
a_Bw_close_client(Web->bw, Client->Key);
}
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Generic MIME handler for GIF, JPEG and PNG.
+ * Sets a_Dicache_callback as the cache-client,
+ * and also sets the image decoder.
+ *
+ * Parameters:
+ * Type: MIME type
+ * Ptr: points to a Web structure
+ * Call: Dillo calls this with more data/eod
+ * Data: Decoding data structure
+ */
+static void *Dicache_image(int ImgType, const char *MimeType, void *Ptr,
+ CA_Callback_t *Call, void **Data)
+{
+ DilloWeb *web = Ptr;
+ DICacheEntry *DicEntry;
+
+ dReturn_val_if_fail(MimeType && Ptr, NULL);
+
+ if (!web->Image) {
+ web->Image = a_Image_new(NULL, web->bgColor);
+ a_Image_ref(web->Image);
+ }
+
+ DicEntry = a_Dicache_get_entry(web->url, DIC_Last);
+ if (!DicEntry) {
+ /* Let's create an entry for this image... */
+ DicEntry = Dicache_add_entry(web->url);
+ DicEntry->DecoderData =
+ (ImgType == DIC_Png) ?
+ a_Png_new(web->Image, DicEntry->url, DicEntry->version) :
+ (ImgType == DIC_Gif) ?
+ a_Gif_new(web->Image, DicEntry->url, DicEntry->version) :
+ (ImgType == DIC_Jpeg) ?
+ a_Jpeg_new(web->Image, DicEntry->url, DicEntry->version) :
+ NULL;
+ } else {
+ /* Repeated image */
+ a_Dicache_ref(DicEntry->url, DicEntry->version);
+ }
+ DicEntry->Decoder = (ImgType == DIC_Png) ? (CA_Callback_t)a_Png_callback :
+ (ImgType == DIC_Gif) ? (CA_Callback_t)a_Gif_callback :
+ (ImgType == DIC_Jpeg) ? (CA_Callback_t)a_Jpeg_callback:
+ NULL;
+ *Data = DicEntry->DecoderData;
+ *Call = (CA_Callback_t) a_Dicache_callback;
+
+ return (web->Image->dw);
+}
+
+/*
+ * PNG wrapper for Dicache_image()
+ */
+void *a_Dicache_png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data)
+{
+ return Dicache_image(DIC_Png, Type, Ptr, Call, Data);
+}
+
+/*
+ * GIF wrapper for Dicache_image()
+ */
+void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data)
+{
+ return Dicache_image(DIC_Gif, Type, Ptr, Call, Data);
+}
+
+/*
+ * JPEG wrapper for Dicache_image()
+ */
+void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data)
+{
+ return Dicache_image(DIC_Jpeg, Type, Ptr, Call, Data);
+}
+
+/*
+ * This function is a cache client; (but feeds its clients from dicache)
+ */
+void a_Dicache_callback(int Op, CacheClient_t *Client)
+{
+ uint_t i;
+ DilloWeb *Web = Client->Web;
+ DilloImage *Image = Web->Image;
+ DICacheEntry *DicEntry = a_Dicache_get_entry(Web->url, DIC_Last);
+
+ dReturn_if_fail ( DicEntry != NULL );
+
+ /* Copy the version number in the Client */
+ if (Client->Version == 0)
+ Client->Version = DicEntry->version;
+
+ /* Only call the decoder when necessary */
+ if (Op == CA_Send && DicEntry->State < DIC_Close &&
+ DicEntry->DecodedSize < Client->BufSize) {
+ DicEntry->Decoder(Op, Client);
+ DicEntry->DecodedSize = Client->BufSize;
+ } else if (Op == CA_Close || Op == CA_Abort) {
+ if (DicEntry->State < DIC_Close) {
+ DicEntry->Decoder(Op, Client);
+ } else {
+ a_Dicache_close(DicEntry->url, DicEntry->version, Client);
+ }
+ }
+
+ /* when the data stream is not an image 'v_imgbuf' remains NULL */
+ if (Op == CA_Send && DicEntry->v_imgbuf) {
+ if (Image->height == 0 && DicEntry->State >= DIC_SetParms) {
+ /* Set parms */
+ a_Image_set_parms(
+ Image, DicEntry->v_imgbuf, DicEntry->url,
+ DicEntry->version, DicEntry->width, DicEntry->height,
+ DicEntry->type);
+ }
+ if (DicEntry->State == DIC_Write) {
+ if (DicEntry->ScanNumber == Image->ScanNumber) {
+ for (i = 0; i < DicEntry->height; ++i)
+ if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) &&
+ !a_Bitvec_get_bit(Image->BitVec, (int)i) )
+ a_Image_write(Image, i);
+ } else {
+ for (i = 0; i < DicEntry->height; ++i) {
+ if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) ||
+ !a_Bitvec_get_bit(Image->BitVec, (int)i) ||
+ DicEntry->ScanNumber > Image->ScanNumber + 1) {
+ a_Image_write(Image, i);
+ }
+ if (!a_Bitvec_get_bit(DicEntry->BitVec, (int)i))
+ a_Bitvec_clear_bit(Image->BitVec, (int)i);
+ }
+ Image->ScanNumber = DicEntry->ScanNumber;
+ }
+ }
+ } else if (Op == CA_Close || Op == CA_Abort) {
+ a_Image_close(Image);
+ a_Bw_close_client(Web->bw, Client->Key);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
/*
* Free the imgbuf (RGB data) of unused entries.
*/
@@ -432,12 +533,13 @@ void a_Dicache_cleanup(void)
DICacheNode *node;
DICacheEntry *entry;
+ _MSG("a_Dicache_cleanup\n");
for (i = 0; i < dList_length(CachedIMGs); ++i) {
node = dList_nth_data(CachedIMGs, i);
/* iterate each entry of this node */
for (entry = node->first; entry; entry = entry->next) {
if (entry->v_imgbuf &&
- a_Image_imgbuf_last_reference(entry->v_imgbuf)) {
+ a_Imgbuf_last_reference(entry->v_imgbuf)) {
/* free this unused entry */
if (entry->next) {
Dicache_remove(node->url, entry->version);
@@ -467,12 +569,11 @@ void a_Dicache_freeall(void)
while ((entry = node->first)) {
node->first = entry->next;
dFree(entry->cmap);
- dFree(entry->linebuf);
a_Bitvec_free(entry->BitVec);
- a_Image_imgbuf_unref(entry->v_imgbuf);
+ a_Imgbuf_unref(entry->v_imgbuf);
dicache_size_total -= entry->TotalSize;
}
- dList_remove(CachedIMGs, node);
+ dList_remove_fast(CachedIMGs, node);
a_Url_free(node->url);
dFree(node);
}
diff --git a/src/dicache.h b/src/dicache.h
index 6cbcf3a5..70adb6c0 100644
--- a/src/dicache.h
+++ b/src/dicache.h
@@ -10,6 +10,10 @@ extern "C" {
#include "image.hh"
#include "cache.h"
+/* Symbolic name to request the last version of an image */
+#define DIC_Last -1
+
+
/* These will reflect the entry's "state" */
typedef enum {
DIC_Empty, /* Just created the entry */
@@ -27,10 +31,8 @@ struct _DICacheEntry {
uint_t width, height; /* As taken from image data */
DilloImgType type; /* Image type */
uchar_t *cmap; /* Color map */
- uchar_t *linebuf; /* Decompressed RGB buffer for one line */
void *v_imgbuf; /* Void pointer to an Imgbuf object */
- size_t TotalSize; /* Amount of memory the image takes up */
- int Y; /* Current decoding row */
+ uint_t TotalSize; /* Amount of memory the image takes up */
uint_t ScanNumber; /* Current decoding scan */
bitvec_t *BitVec; /* Bit vector for decoded rows */
DicEntryState State; /* Current status for this entry */
@@ -38,15 +40,24 @@ struct _DICacheEntry {
int version; /* Version number, used for different
versions of the same URL image */
+ CA_Callback_t Decoder; /* Client function */
+ void *DecoderData; /* Client function data */
+ uint_t DecodedSize; /* Size of already decoded data */
+
DICacheEntry *next; /* Link to the next "newer" version */
};
void a_Dicache_init (void);
-DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url);
-DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url);
+DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url, int version);
+void *a_Dicache_png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data);
+void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data);
+void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data);
void a_Dicache_callback(int Op, CacheClient_t *Client);
void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
@@ -54,9 +65,8 @@ void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
const uchar_t *cmap, uint_t num_colors,
int num_colors_max, int bg_index);
-void a_Dicache_new_scan(DilloImage *image, const DilloUrl *url, int version);
-void a_Dicache_write(DilloImage *Image, DilloUrl *url, int version,
- const uchar_t *buf, uint_t Y);
+void a_Dicache_new_scan(const DilloUrl *url, int version);
+void a_Dicache_write(DilloUrl *url, int version, const uchar_t *buf, uint_t Y);
void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client);
void a_Dicache_invalidate_entry(const DilloUrl *Url);
diff --git a/src/dillo.cc b/src/dillo.cc
index 7e29ac32..a42b0dab 100644
--- a/src/dillo.cc
+++ b/src/dillo.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -26,16 +25,19 @@
#include <fltk/Window.h>
#include <fltk/TabGroup.h>
+#include <fltk/Font.h>
#include <fltk/run.h>
#include "msg.h"
-#include "dir.h"
+#include "paths.hh"
#include "uicmd.hh"
#include "prefs.h"
+#include "prefsparser.hh"
+#include "keys.hh"
#include "bw.h"
#include "misc.h"
-#include "nav.h"
+#include "history.h"
#include "dns.h"
#include "web.hh"
@@ -44,37 +46,174 @@
#include "capi.h"
#include "dicache.h"
#include "cookies.h"
+#include "auth.h"
+/*
+ * Command line options structure
+ */
+typedef enum {
+ DILLO_CLI_NONE = 0,
+ DILLO_CLI_XID = 1 << 0,
+ DILLO_CLI_FULLWINDOW = 1 << 1,
+ DILLO_CLI_HELP = 1 << 2,
+ DILLO_CLI_VERSION = 1 << 3,
+ DILLO_CLI_LOCAL = 1 << 4,
+ DILLO_CLI_GEOMETRY = 1 << 5,
+ DILLO_CLI_ERROR = 1 << 15,
+} OptID;
+
+typedef struct {
+ const char *shortopt;
+ const char *longopt;
+ int opt_argc; /* positive: mandatory, negative: optional */
+ OptID id;
+ const char *help;
+} CLI_options;
+
+static const CLI_options Options[] = {
+ {"-f", "--fullwindow", 0, DILLO_CLI_FULLWINDOW,
+ " -f, --fullwindow Start in full window mode: hide address bar,\n"
+ " navigation buttons, menu, and status bar."},
+ {"-g", "-geometry", 1, DILLO_CLI_GEOMETRY,
+ " -g, -geometry GEO Set initial window position where GEO is\n"
+ " WxH[{+-}X{+-}Y]"},
+ {"-h", "--help", 0, DILLO_CLI_HELP,
+ " -h, --help Display this help text and exit."},
+ {"-l", "--local", 0, DILLO_CLI_LOCAL,
+ " -l, --local Don't load images for these URL(s)."},
+ {"-v", "--version", 0, DILLO_CLI_VERSION,
+ " -v, --version Display version info and exit."},
+ {"-x", "--xid", 1, DILLO_CLI_XID,
+ " -x, --xid XID Open first Dillo window in an existing\n"
+ " window whose window ID is XID."},
+ {NULL, NULL, 0, DILLO_CLI_NONE, NULL}
+};
+
+/*
+ * Print help text generated from the options structure
+ */
+static void printHelp(const char *cmdname, const CLI_options *options)
+{
+ printf("Usage: %s [OPTION]... [--] [URL|FILE]...\n"
+ "Options:\n", cmdname);
+ while (options && options->help) {
+ printf("%s\n", options->help);
+ options++;
+ }
+ printf(" URL URL to browse.\n"
+ " FILE Local FILE to view.\n"
+ "\n");
+}
+
+/*
+ * Return the maximum number of option arguments
+ */
+static int numOptions(const CLI_options *options)
+{
+ int i, max;
+
+ for (i = 0, max = 0; options[i].shortopt; i++)
+ if (abs(options[i].opt_argc) > max)
+ max = abs(options[i].opt_argc);
+ return max;
+}
+
+/*
+ * Get next command line option.
+ */
+static OptID getCmdOption(const CLI_options *options, int argc, char **argv,
+ char **opt_argv, int *idx)
+{
+ typedef enum { O_SEARCH, O_FOUND, O_NOTFOUND, O_DONE } State;
+ OptID opt_id = DILLO_CLI_NONE;
+ int i = 0;
+ State state = O_SEARCH;
+
+ if (*idx >= argc) {
+ state = O_DONE;
+ } else {
+ state = O_NOTFOUND;
+ for (i = 0; options[i].shortopt; i++) {
+ if (strcmp(options[i].shortopt, argv[*idx]) == 0 ||
+ strcmp(options[i].longopt, argv[*idx]) == 0) {
+ state = O_FOUND;
+ ++*idx;
+ break;
+ }
+ }
+ }
+ if (state == O_FOUND) {
+ int n_arg = options[i].opt_argc;
+ opt_id = options[i].id;
+ /* Find the required/optional arguments of the option */
+ for (i = 0; *idx < argc && i < abs(n_arg) && argv[*idx][0] != '-'; i++)
+ opt_argv[i] = argv[(*idx)++];
+ opt_argv[i] = NULL;
+
+ /* Optional arguments have opt_argc < 0 */
+ if (i < n_arg) {
+ fprintf(stderr, "Option %s requires %d argument%s\n",
+ argv[*idx-i-1], n_arg, (n_arg == 1) ? "" : "s");
+ opt_id = DILLO_CLI_ERROR;
+ }
+ }
+ if (state == O_NOTFOUND) {
+ if (strcmp(argv[*idx], "--") == 0)
+ (*idx)++;
+ else if (argv[*idx][0] == '-') {
+ fprintf(stderr, "Command line option \"%s\" not recognized.\n",
+ argv[*idx]);
+ opt_id = DILLO_CLI_ERROR;
+ }
+ }
+ return opt_id;
+}
+
+/*
+ * Tell the user if default/pref fonts can't be found.
+ */
+static void checkFont(const char *name, const char *type)
+{
+ if (::fltk::font(name) == NULL)
+ MSG_WARN("preferred %s font \"%s\" not found.\n", type, name);
+}
+
+static void checkPreferredFonts()
+{
+ checkFont(prefs.font_sans_serif, "sans-serif");
+ checkFont(prefs.font_serif, "serif");
+ checkFont(prefs.font_monospace, "monospace");
+ checkFont(prefs.font_cursive, "cursive");
+ checkFont(prefs.font_fantasy, "fantasy");
+}
/*
* Given a command line argument, build a DilloUrl for it.
*/
-static DilloUrl *Dillo_make_start_url(char *str)
+static DilloUrl *makeStartUrl(char *str, bool local)
{
char *url_str, *p;
DilloUrl *start_url;
- int is_file = FALSE;
/* Relative path to a local file? */
- p = (*str == '/') ? dStrdup(str) : dStrconcat(a_Dir_get_owd(),"/",str,NULL);
+ p = (*str == '/') ? dStrdup(str) :
+ dStrconcat(Paths::getOldWorkingDir(), "/", str, NULL);
if (access(p, F_OK) == 0) {
/* absolute path may have non-URL characters */
url_str = a_Misc_escape_chars(p, "% ");
- is_file = TRUE;
+ start_url = a_Url_new(url_str + 1, "file:/");
} else {
/* Not a file, filter URL string */
url_str = a_Url_string_strip_delimiters(str);
- }
- dFree(p);
-
- if (is_file) {
- start_url = a_Url_new(url_str + 1, "file:/");
- } else {
start_url = a_Url_new(url_str, NULL);
}
+ dFree(p);
dFree(url_str);
+ if (local)
+ a_Url_set_flags(start_url, URL_FLAGS(start_url) | URL_SpamSafe);
+
return start_url;
}
@@ -83,15 +222,79 @@ static DilloUrl *Dillo_make_start_url(char *str)
*/
int main(int argc, char **argv)
{
+ uint_t opt_id;
+ uint_t options_got = 0;
+ uint32_t xid = 0;
+ int idx = 1;
+ int xpos = PREFS_GEOMETRY_DEFAULT_XPOS, ypos = PREFS_GEOMETRY_DEFAULT_YPOS,
+ width = PREFS_GEOMETRY_DEFAULT_WIDTH,
+ height = PREFS_GEOMETRY_DEFAULT_HEIGHT;
+ char **opt_argv;
+ FILE *fp;
+
srand((uint_t)(time(0) ^ getpid()));
// Some OSes exit dillo without this (not GNU/Linux).
signal(SIGPIPE, SIG_IGN);
- // Initialize internal modules
- a_Dir_init();
+ /* Handle command line options */
+ opt_argv = dNew0(char*, numOptions(Options) + 1);
+ while ((opt_id = getCmdOption(Options, argc, argv, opt_argv, &idx))) {
+ options_got |= opt_id;
+ switch (opt_id) {
+ case DILLO_CLI_FULLWINDOW:
+ case DILLO_CLI_LOCAL:
+ break;
+ case DILLO_CLI_XID:
+ {
+ char *end;
+ xid = strtol(opt_argv[0], &end, 0);
+ if (*end) {
+ fprintf(stderr, "XID argument \"%s\" not valid.\n",opt_argv[0]);
+ return 2;
+ }
+ break;
+ }
+ case DILLO_CLI_GEOMETRY:
+ if (!a_Misc_parse_geometry(opt_argv[0],&xpos,&ypos,&width,&height)){
+ fprintf(stderr, "geometry argument \"%s\" not valid. Must be of "
+ "the form WxH[{+-}X{+-}Y].\n", opt_argv[0]);
+ return 2;
+ }
+ break;
+ case DILLO_CLI_VERSION:
+ puts("Dillo version " VERSION);
+ return 0;
+ case DILLO_CLI_HELP:
+ printHelp(argv[0], Options);
+ return 0;
+ default:
+ printHelp(argv[0], Options);
+ return 2;
+ }
+ }
+ dFree(opt_argv);
+
+ // set the default values for the preferences
a_Prefs_init();
- a_Dir_check_dillorc_directory(); /* and create if not present */
+
+ // create ~/.dillo if not present
+ Paths::init();
+
+ // initialize default key bindings
+ Keys::init();
+
+ // parse dillorc
+ if ((fp = Paths::getPrefsFP(PATHS_RC_PREFS))) {
+ PrefsParser::parse(fp);
+ }
+ // parse keysrc
+ if ((fp = Paths::getPrefsFP(PATHS_RC_KEYS))) {
+ Keys::parse(fp);
+ }
+ dLib_show_messages(prefs.show_msg);
+
+ // initialize internal modules
a_Dpi_init();
a_Dns_init();
a_Web_init();
@@ -101,6 +304,17 @@ int main(int argc, char **argv)
a_Dicache_init();
a_Bw_init();
a_Cookies_init();
+ a_Auth_init();
+
+ /* command line options override preferences */
+ if (options_got & DILLO_CLI_FULLWINDOW)
+ prefs.fullwindow_start = TRUE;
+ if (options_got & DILLO_CLI_GEOMETRY) {
+ prefs.width = width;
+ prefs.height = height;
+ prefs.xpos = xpos;
+ prefs.ypos = ypos;
+ }
// Sets WM_CLASS hint on X11
fltk::Window::xclass("dillo");
@@ -108,9 +322,18 @@ int main(int argc, char **argv)
// WORKAROUND: sometimes the default pager triggers redraw storms
fltk::TabGroup::default_pager(fltk::PAGER_SHRINK);
+ checkPreferredFonts();
+ /* use preferred font for UI */
+ fltk::Font *dfont = fltk::font(prefs.font_sans_serif, 0);
+ if (dfont) {
+ fltk::Widget::default_style->textfont(dfont);
+ fltk::Widget::default_style->labelfont(dfont);
+ }
+
// Create a new UI/bw pair
- BrowserWindow *bw = a_UIcmd_browser_window_new(0, 0, NULL);
+ BrowserWindow *bw = a_UIcmd_browser_window_new(0, 0, xid, NULL);
+ /* Proxy authentication */
if (prefs.http_proxyuser && !a_Http_proxy_auth()) {
const char *passwd = a_UIcmd_get_passwd(prefs.http_proxyuser);
if (passwd) {
@@ -120,24 +343,50 @@ int main(int argc, char **argv)
}
}
- if (argc == 2) {
- DilloUrl *url = Dillo_make_start_url(argv[1]);
- a_UIcmd_open_urlstr(bw, URL_STR(url));
- a_Url_free(url);
- } else if (argc == 6) {
- // WORKAROUND: sylpheed execs "dillo -l -f -x XID URL"
- if (strcmp(argv[1], "-l") == 0 && strcmp(argv[2], "-f") == 0 &&
- strcmp(argv[3], "-x") == 0) {
- a_UIcmd_set_images_enabled(bw, FALSE);
- DilloUrl *url = Dillo_make_start_url(argv[5]);
- a_Url_set_flags(url, URL_FLAGS(url) & URL_SpamSafe);
- a_UIcmd_open_urlstr(bw, URL_STR(url));
- a_Url_free(url);
- }
+ /* Open URLs/files */
+ const bool local = options_got & DILLO_CLI_LOCAL;
+
+ if (idx == argc) {
+ /* No URLs/files on cmdline. Send startup screen */
+ a_UIcmd_open_url(bw, prefs.start_page);
} else {
- /* Send startup screen */
- a_Nav_push(bw, prefs.start_page);
+ for (int i = idx; i < argc; i++) {
+ DilloUrl *start_url = makeStartUrl(argv[i], local);
+
+ if (i > idx) {
+ if (prefs.middle_click_opens_new_tab) {
+ /* user must prefer tabs */
+ const int focus = 1;
+ a_UIcmd_open_url_nt(bw, start_url, focus);
+ } else {
+ a_UIcmd_open_url_nw(bw, start_url);
+ }
+ } else {
+ a_UIcmd_open_url(bw, start_url);
+ }
+ a_Url_free(start_url);
+ }
}
- return fltk::run();
+ fltk::run();
+
+ /*
+ * Memory deallocating routines
+ * (This can be left to the OS, but we'll do it, with a view to test
+ * and fix our memory management)
+ */
+ a_Cookies_freeall();
+ a_Cache_freeall();
+ a_Dicache_freeall();
+ a_Http_freeall();
+ a_Dns_freeall();
+ a_History_freeall();
+ a_Prefs_freeall();
+ Keys::free();
+ Paths::free();
+ /* TODO: auth, css */
+
+ //a_Dpi_dillo_exit();
+ MSG("Dillo: normal exit!\n");
+ return 0;
}
diff --git a/src/dir.c b/src/dir.c
deleted file mode 100644
index f7b2644d..00000000
--- a/src/dir.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * File: dir.c
- *
- * Copyright 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- */
-
-#include <unistd.h>
-#include <errno.h>
-#include <sys/stat.h>
-
-#include "msg.h"
-#include "../dlib/dlib.h"
-
-
-/*
- * Local data
- */
-/* Dillo works from an unmounted directory (/tmp). */
-static char *OldWorkingDirectory = NULL;
-
-/*
- * Change current working directory to "/tmp".
- */
-void a_Dir_init(void)
-{
- dFree(OldWorkingDirectory);
- OldWorkingDirectory = dGetcwd();
- chdir("/tmp");
-}
-
-/*
- * Return the initial current working directory in a string.
- */
-char *a_Dir_get_owd(void)
-{
- return OldWorkingDirectory;
-}
-
-/*
- * Free memory
- */
-void a_Dir_free(void)
-{
- dFree(OldWorkingDirectory);
-}
-
-/*
- * Check if '~/.dillo' directory exists.
- * If not, try to create it.
- */
-void a_Dir_check_dillorc_directory(void)
-{
- char *dir;
- struct stat st;
-
- dir = dStrconcat(dGethomedir(), "/.dillo", NULL);
- if (stat(dir, &st) == -1) {
- if (errno == ENOENT) {
- MSG("Dillo: creating directory %s.\n", dir);
- if (mkdir(dir, 0700) < 0) {
- MSG("Dillo: error creating directory %s: %s\n", dir,
- dStrerror(errno));
- }
- } else {
- MSG("Dillo: error reading %s: %s\n", dir, dStrerror(errno));
- }
- }
- dFree(dir);
-}
-
diff --git a/src/dir.h b/src/dir.h
deleted file mode 100644
index 19d69713..00000000
--- a/src/dir.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef __DIR_H__
-#define __DIR_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
-void a_Dir_init(void);
-char *a_Dir_get_owd(void);
-void a_Dir_free(void);
-void a_Dir_check_dillorc_directory(void);
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* __DIR_H__ */
-
diff --git a/src/djpeg.h b/src/djpeg.h
new file mode 100644
index 00000000..32419e4b
--- /dev/null
+++ b/src/djpeg.h
@@ -0,0 +1,19 @@
+#ifndef __JPEG_H__
+#define __JPEG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "url.h"
+#include "image.hh"
+
+
+void *a_Jpeg_new(DilloImage *Image, DilloUrl *url, int version);
+void a_Jpeg_callback(int Op, void *data);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* !__JPEG_H__ */
diff --git a/src/dns.c b/src/dns.c
index c783366f..980586c6 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -13,17 +13,26 @@
* Non blocking pthread-handled Dns scheme
*/
-#include <pthread.h>
+
+/*
+ * Uncomment the following line for debugging or gprof profiling.
+ */
+/* #undef D_DNS_THREADED */
+
+#ifdef D_DNS_THREADED
+# include <pthread.h>
+#endif
+
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
-#include <signal.h>
#include <string.h>
#include "msg.h"
@@ -31,11 +40,6 @@
#include "list.h"
#include "timeout.hh"
-/*
- * Uncomment the following line for debugging or gprof profiling.
- */
-/* #undef D_DNS_THREADED */
-
/* Maximum dns resolving threads */
#ifdef D_DNS_THREADED
@@ -84,7 +88,6 @@ static GDnsCache *dns_cache;
static int dns_cache_size, dns_cache_size_max;
static GDnsQueue *dns_queue;
static int dns_queue_size, dns_queue_size_max;
-static bool_t ipv6_enabled;
/* ----------------------------------------------------------------------
@@ -197,16 +200,14 @@ void a_Dns_init(void)
#endif
}
- /* IPv6 test */
- ipv6_enabled = FALSE;
#ifdef ENABLE_IPV6
+ /* IPv6 test */
{
/* If the IPv6 address family is not available there is no point
wasting time trying to connect to v6 addresses. */
int fd = socket(AF_INET6, SOCK_STREAM, 0);
if (fd >= 0) {
close(fd);
- ipv6_enabled = TRUE;
}
}
#endif
@@ -267,12 +268,15 @@ static void *Dns_server(void *data)
int channel = VOIDP2INT(data);
struct addrinfo hints, *res0;
int error;
+ Dlist *hosts;
+ size_t length, i;
+ char addr_string[40];
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- Dlist *hosts = dList_new(2);
+ hosts = dList_new(2);
_MSG("Dns_server: starting...\n ch: %d host: %s\n",
channel, dns_server[channel].hostname);
@@ -306,8 +310,18 @@ static void *Dns_server(void *data)
}
/* tell our findings */
- MSG("Dns_server [%d]: %s is %p\n", channel,
- dns_server[channel].hostname, hosts);
+ MSG("Dns_server [%d]: %s is", channel,
+ dns_server[channel].hostname);
+ if ((length = dList_length(hosts))) {
+ for (i = 0; i < length; i++) {
+ a_Dns_dillohost_to_string(dList_nth_data(hosts, i),
+ addr_string, sizeof(addr_string));
+ MSG(" %s", addr_string);
+ }
+ MSG("\n");
+ } else {
+ MSG(" (nil)\n");
+ }
dns_server[channel].addr_list = hosts;
dns_server[channel].ip_ready = TRUE;
@@ -367,7 +381,7 @@ void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data)
break;
if (i < dns_cache_size) {
- /* already resolved, call the Callback inmediately. */
+ /* already resolved, call the Callback immediately. */
cb_func(0, dns_cache[i].addr_list, cb_data);
} else if ((i = Dns_queue_find(hostname)) != -1) {
@@ -469,15 +483,35 @@ static void Dns_timeout_client(void *data)
* Dns memory-deallocation
* (Call this one at exit time)
* The Dns_queue is deallocated at execution time (no need to do that here)
- * 'dns_cache' is the only one that grows dinamically
+ * 'dns_cache' is the only one that grows dynamically
*/
void a_Dns_freeall(void)
{
- int i;
+ int i, j;
for ( i = 0; i < dns_cache_size; ++i ){
dFree(dns_cache[i].hostname);
+ for ( j = 0; j < dList_length(dns_cache[i].addr_list); ++j)
+ dFree(dList_nth_data(dns_cache[i].addr_list, j));
+ dList_free(dns_cache[i].addr_list);
}
dFree(dns_cache);
}
+/*
+ * Writes a string representation of the given DilloHost
+ * into dst. dst will be \0 terminated.
+ * Please note that dst must be at least 40 bytes long for IPv6
+ * addresses.
+ */
+void a_Dns_dillohost_to_string(DilloHost *host, char *dst, size_t size)
+{
+ if (!inet_ntop(host->af, host->data, dst, size)) {
+ switch (errno) {
+ case EAFNOSUPPORT:
+ snprintf(dst, size, "Unknown address family");
+ case ENOSPC:
+ snprintf(dst, size, "Buffer too small");
+ }
+ }
+}
diff --git a/src/dns.h b/src/dns.h
index 1749044f..78f8cf18 100644
--- a/src/dns.h
+++ b/src/dns.h
@@ -14,7 +14,11 @@ void a_Dns_init (void);
void a_Dns_freeall(void);
void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data);
-#define DILLO_ADDR_MAX sizeof(struct in6_addr)
+#ifdef ENABLE_IPV6
+# define DILLO_ADDR_MAX sizeof(struct in6_addr)
+#else
+# define DILLO_ADDR_MAX sizeof(struct in_addr)
+#endif
typedef struct _DilloHost
{
@@ -22,7 +26,7 @@ typedef struct _DilloHost
int alen;
char data[DILLO_ADDR_MAX];
} DilloHost;
-
+void a_Dns_dillohost_to_string(DilloHost *host, char *dst, size_t size);
#ifdef __cplusplus
}
diff --git a/src/doctree.hh b/src/doctree.hh
new file mode 100644
index 00000000..ef7faa7c
--- /dev/null
+++ b/src/doctree.hh
@@ -0,0 +1,72 @@
+#ifndef __DOCTREE_HH__
+#define __DOCTREE_HH__
+
+#include "lout/misc.hh"
+
+class DoctreeNode {
+ public:
+ DoctreeNode *parent;
+ int num; // unique ascending id
+ int element;
+ lout::misc::SimpleVector<char*> *klass;
+ const char *pseudo;
+ const char *id;
+
+ DoctreeNode () {
+ parent = NULL;
+ klass = NULL;
+ pseudo = NULL;
+ id = NULL;
+ element = 0;
+ };
+};
+
+/**
+ * \brief HTML document tree interface.
+ *
+ * The Doctree class defines the interface to the parsed HTML document tree
+ * as it is used for CSS selector matching.
+ * Currently the Doctree can be represented as stack, however to support
+ * CSS adjacent siblings or for future JavaScript support it may have to
+ * be extended to a real tree.
+ */
+class Doctree {
+ private:
+ DoctreeNode *topNode;
+ int num;
+
+ public:
+ Doctree () {
+ topNode = NULL;
+ num = 0;
+ };
+ ~Doctree () { while (top ()) pop (); };
+ DoctreeNode *push () {
+ DoctreeNode *dn = new DoctreeNode ();
+ dn->parent = topNode;
+ dn->num = num++;
+ topNode = dn;
+ return dn;
+ };
+ void pop () {
+ DoctreeNode *dn = topNode;
+ if (dn) {
+ dFree ((void*) dn->id);
+ if (dn->klass) {
+ for (int i = 0; i < dn->klass->size (); i++)
+ dFree (dn->klass->get(i));
+ delete dn->klass;
+ }
+ topNode = dn->parent;
+ delete dn;
+ }
+ };
+ inline DoctreeNode *top () {
+ return topNode;
+ };
+ inline DoctreeNode *parent (const DoctreeNode *node) {
+ return node->parent;
+ };
+};
+
+#endif
diff --git a/src/dpiapi.c b/src/dpiapi.c
index cf32ed3f..2068146c 100644
--- a/src/dpiapi.c
+++ b/src/dpiapi.c
@@ -41,6 +41,7 @@ static void Dpiapi_dialog_answer_cb(BrowserWindow *bw, int answer)
/* Send answer */
a_Capi_dpi_send_cmd(NULL, bw, cmd, dialog_server, 0);
+ dFree(cmd);
}
/*
@@ -60,12 +61,12 @@ void a_Dpiapi_dialog(BrowserWindow *bw, char *server, char *dpip_tag)
/* other options can be parsed the same way */
dpip_tag_len = strlen(dpip_tag);
- question = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "msg");
- alt1 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt1");
- alt2 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt2");
- alt3 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt3");
- alt4 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt4");
- alt5 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt5");
+ question = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "msg");
+ alt1 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt1");
+ alt2 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt2");
+ alt3 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt3");
+ alt4 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt4");
+ alt5 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt5");
ret = a_Dialog_choice5(question, alt1, alt2, alt3, alt4, alt5);
/* As choice5 is modal, call the callback function directly. */
diff --git a/src/dpng.h b/src/dpng.h
new file mode 100644
index 00000000..a9ee8820
--- /dev/null
+++ b/src/dpng.h
@@ -0,0 +1,19 @@
+#ifndef __PNG_H__
+#define __PNG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "url.h"
+#include "image.hh"
+
+
+void *a_Png_new(DilloImage *Image, DilloUrl *url, int version);
+void a_Png_callback(int Op, CacheClient_t *Client);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* !__PNG_H__ */
diff --git a/src/findbar.cc b/src/findbar.cc
index 5d1c6245..8cca52ba 100644
--- a/src/findbar.cc
+++ b/src/findbar.cc
@@ -18,6 +18,8 @@
#include "uicmd.hh"
#include "bw.h"
+using namespace fltk;
+
/*
* Local sub class
* (Used to handle escape in the findbar, may also avoid some shortcuts).
@@ -63,7 +65,22 @@ void Findbar::search_cb(Widget *, void *vfb)
if (key[0] != '\0')
a_UIcmd_findtext_search(a_UIcmd_get_bw_by_widget(fb),
- key, case_sens);
+ key, case_sens, false);
+}
+
+/*
+ * Find previous occurrence of input key
+ */
+void Findbar::searchBackwards_cb(Widget *, void *vfb)
+{
+ Findbar *fb = (Findbar *)vfb;
+ const char *key = fb->i->text();
+ bool case_sens = fb->check_btn->value();
+
+ if (key[0] != '\0') {
+ a_UIcmd_findtext_search(a_UIcmd_get_bw_by_widget(fb),
+ key, case_sens, true);
+ }
}
/*
@@ -74,7 +91,7 @@ void Findbar::search_cb2(Widget *widget, void *vfb)
/*
* Somehow fltk even regards the first loss of focus for the
* window as a WHEN_ENTER_KEY_ALWAYS event.
- */
+ */
if (event_key() == ReturnKey)
search_cb(widget, vfb);
}
@@ -96,7 +113,7 @@ Findbar::Findbar(int width, int height) :
int button_width = 70;
int gap = 2;
int border = 2;
- int input_width = width - (2 * border + 3 * (button_width + gap));
+ int input_width = width - (2 * border + 4 * (button_width + gap));
int x = border;
height -= 2 * border;
@@ -107,7 +124,6 @@ Findbar::Findbar(int width, int height) :
hide_btn = new HighlightButton(x, border, 16, height, 0);
hideImg = new xpmImage(new_s_xpm);
hide_btn->image(hideImg);
- hide_btn->tooltip("Hide");
x += 16 + gap;
hide_btn->callback(hide_cb, this);
hide_btn->clear_tab_to_focus();
@@ -121,21 +137,33 @@ Findbar::Findbar(int width, int height) :
i->clear_tab_to_focus();
i->set_click_to_focus();
- // TODO: search previous would be nice
next_btn = new HighlightButton(x, border, button_width, height, "Next");
x += button_width + gap;
- next_btn->tooltip("Find next occurrence of the search phrase");
next_btn->add_shortcut(ReturnKey);
next_btn->add_shortcut(KeypadEnter);
next_btn->callback(search_cb, this);
next_btn->clear_tab_to_focus();
+ prev_btn= new HighlightButton(x, border, button_width, height, "Previous");
+ prev_btn->add_shortcut(SHIFT+ReturnKey);
+ prev_btn->callback(searchBackwards_cb, this);
+ prev_btn->clear_tab_to_focus();
+ x += button_width + gap;
+
check_btn = new CheckButton(x, border, 2*button_width, height,
"Case-sensitive");
check_btn->clear_tab_to_focus();
x += 2 * button_width + gap;
end();
+
+ if (prefs.show_tooltip) {
+ hide_btn->tooltip("Hide");
+ next_btn->tooltip("Find next occurrence of the search phrase\n"
+ "shortcut: Enter");
+ prev_btn->tooltip("Find previous occurrence of the search phrase\n"
+ "shortcut: Shift+Enter");
+ }
}
Findbar::~Findbar()
diff --git a/src/findbar.hh b/src/findbar.hh
index 1e8c8d66..ba7ed8ed 100644
--- a/src/findbar.hh
+++ b/src/findbar.hh
@@ -9,21 +9,20 @@
#include <fltk/Group.h>
#include <fltk/CheckButton.h>
-using namespace fltk;
-
/*
* Searchbar to find text in page.
*/
-class Findbar : public Group {
- Button *clrb;
- HighlightButton *hide_btn, *next_btn;
- CheckButton *check_btn;
- xpmImage *hideImg;
- Input *i;
-
- static void search_cb (Widget *, void *);
- static void search_cb2 (Widget *, void *);
- static void hide_cb (Widget *, void *);
+class Findbar : public fltk::Group {
+ fltk::Button *clrb;
+ fltk::HighlightButton *hide_btn, *next_btn, *prev_btn;
+ fltk::CheckButton *check_btn;
+ fltk::xpmImage *hideImg;
+ fltk::Input *i;
+
+ static void search_cb (fltk::Widget *, void *);
+ static void searchBackwards_cb (fltk::Widget *, void *);
+ static void search_cb2 (fltk::Widget *, void *);
+ static void hide_cb (fltk::Widget *, void *);
public:
Findbar(int width, int height);
diff --git a/src/form.cc b/src/form.cc
index 78bc43e8..38afea8f 100644
--- a/src/form.cc
+++ b/src/form.cc
@@ -22,28 +22,28 @@
#include "misc.h"
#include "msg.h"
#include "prefs.h"
-#include "nav.h"
#include "uicmd.hh"
+using namespace lout;
using namespace dw;
using namespace dw::core;
using namespace dw::core::style;
+using namespace dw::core::ui;
/*
- * Forward declarations
+ * Forward declarations
*/
class DilloHtmlReceiver;
class DilloHtmlSelect;
class DilloHtmlOption;
-static dw::core::ui::Embed *Html_input_image(DilloHtml *html,
- const char *tag, int tagsize);
+static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize);
static void Html_option_finish(DilloHtml *html);
/*
- * Typedefs
+ * Typedefs
*/
typedef enum {
@@ -67,7 +67,7 @@ typedef enum {
} DilloHtmlInputType;
/*
- * Class declarations
+ * Class declarations
*/
class DilloHtmlForm {
@@ -75,74 +75,74 @@ class DilloHtmlForm {
friend class DilloHtmlInput;
DilloHtml *html;
- void eventHandler(dw::core::ui::Resource *resource);
- void submit(DilloHtmlInput *input);
- DilloUrl *buildQueryUrl(DilloHtmlInput *input);
+ bool showing_hiddens;
+ bool enabled;
+ void eventHandler(Resource *resource, EventButton *event);
+ DilloUrl *buildQueryUrl(DilloHtmlInput *active_input);
Dstr *buildQueryData(DilloHtmlInput *active_submit);
- char *makeMultipartBoundary(iconv_t encoder, DilloHtmlInput *active_submit);
- Dstr *encodeText(iconv_t encoder, Dstr **input);
- void urlencodeAppend(Dstr *str, const char *val);
- void appendInputUrlencode(Dstr *data,
+ char *makeMultipartBoundary(iconv_t char_encoder,
+ DilloHtmlInput *active_submit);
+ Dstr *encodeText(iconv_t char_encoder, Dstr **input);
+ void strUrlencodeAppend(Dstr *dstr, const char *str);
+ void inputUrlencodeAppend(Dstr *data, const char *name, const char *value);
+ void inputMultipartAppend(Dstr *data, const char *boundary,
const char *name, const char *value);
- void appendInputMultipartFiles(Dstr* data,
- const char *boundary,
+ void filesInputMultipartAppend(Dstr* data, const char *boundary,
const char *name, Dstr *file,
const char *filename);
- void appendInputMultipart(Dstr *data,
- const char *boundary,
- const char *name,
- const char *value);
- void appendClickposUrlencode(Dstr *data, Dstr *name, Dstr *x, Dstr *y);
- void appendClickposMultipart(Dstr *data, const char *boundary, Dstr *name,
- Dstr *x, Dstr *y);
+ void imageInputUrlencodeAppend(Dstr *data, Dstr *name, Dstr *x, Dstr *y);
+ void imageInputMultipartAppend(Dstr *data, const char *boundary, Dstr *name,
+ Dstr *x, Dstr *y);
public: //BUG: for now everything is public
DilloHtmlMethod method;
DilloUrl *action;
- DilloHtmlEnc enc;
+ DilloHtmlEnc content_type;
char *submit_charset;
lout::misc::SimpleVector<DilloHtmlInput*> *inputs;
int num_entry_fields;
- int num_submit_buttons;
DilloHtmlReceiver *form_receiver;
public:
- DilloHtmlForm (DilloHtml *html,
+ DilloHtmlForm (DilloHtml *html,
DilloHtmlMethod method, const DilloUrl *action,
- DilloHtmlEnc enc, const char *charset);
+ DilloHtmlEnc content_type, const char *charset,
+ bool enabled);
~DilloHtmlForm ();
- DilloHtmlInput *getInput (dw::core::ui::Resource *resource);
+ DilloHtmlInput *getInput (Resource *resource);
DilloHtmlInput *getRadioInput (const char *name);
+ void submit(DilloHtmlInput *active_input, EventButton *event);
void reset ();
+ void display_hiddens(bool display);
void addInput(DilloHtmlInput *input, DilloHtmlInputType type);
+ void setEnabled(bool enabled);
};
class DilloHtmlReceiver:
- public dw::core::ui::Resource::ActivateReceiver,
- public dw::core::ui::ButtonResource::ClickedReceiver
+ public Resource::ActivateReceiver,
+ public Resource::ClickedReceiver
{
friend class DilloHtmlForm;
DilloHtmlForm* form;
DilloHtmlReceiver (DilloHtmlForm* form2) { form = form2; }
~DilloHtmlReceiver () { }
- void activate (dw::core::ui::Resource *resource);
- void enter (dw::core::ui::Resource *resource);
- void leave (dw::core::ui::Resource *resource);
- void clicked (dw::core::ui::ButtonResource *resource,
- int buttonNo, int x, int y);
+ void activate (Resource *resource);
+ void enter (Resource *resource);
+ void leave (Resource *resource);
+ void clicked (Resource *resource, EventButton *event);
};
class DilloHtmlInput {
- // DilloHtmlForm::addInput() calls connectTo()
+ // DilloHtmlForm::addInput() calls connectTo()
friend class DilloHtmlForm;
public: //BUG: for now everything is public
DilloHtmlInputType type;
- dw::core::ui::Embed *embed; /* May be NULL (think: hidden input) */
+ Embed *embed; /* May be NULL (think: hidden input) */
char *name;
char *init_str; /* note: some overloading - for buttons, init_str
is simply the value of the button; for text
@@ -154,18 +154,16 @@ public: //BUG: for now everything is public
private:
void connectTo(DilloHtmlReceiver *form_receiver);
- void activate(DilloHtmlForm *form, bool force_submit);
+ void activate(DilloHtmlForm *form, int num_entry_fields,EventButton *event);
void readFile(BrowserWindow *bw);
public:
- DilloHtmlInput (DilloHtmlInputType type,
- dw::core::ui::Embed *embed,
- const char *name,
- const char *init_str,
- bool init_val);
+ DilloHtmlInput (DilloHtmlInputType type, Embed *embed,
+ const char *name, const char *init_str, bool init_val);
~DilloHtmlInput ();
void appendValuesTo(Dlist *values, bool is_active_submit);
void reset();
+ void setEnabled(bool enabled) {if (embed) embed->setEnabled(enabled); };
};
class DilloHtmlSelect {
@@ -178,9 +176,8 @@ public:
DilloHtmlOption *getCurrentOption ();
void addOption (char *value, bool selected, bool enabled);
void ensureSelection ();
- void addOptionsTo (dw::core::ui::SelectionResource *res);
- void appendValuesTo (Dlist *values,
- dw::core::ui::SelectionResource *res);
+ void addOptionsTo (SelectionResource *res);
+ void appendValuesTo (Dlist *values, SelectionResource *res);
};
class DilloHtmlOption {
@@ -190,20 +187,20 @@ public:
bool selected, enabled;
private:
DilloHtmlOption (char *value, bool selected, bool enabled);
- ~DilloHtmlOption ();
+ ~DilloHtmlOption ();
};
/*
- * Form API
+ * Form API
*/
-DilloHtmlForm *a_Html_form_new (DilloHtml *html,
- DilloHtmlMethod method,
+DilloHtmlForm *a_Html_form_new (DilloHtml *html, DilloHtmlMethod method,
const DilloUrl *action,
- DilloHtmlEnc enc,
- const char *charset)
+ DilloHtmlEnc content_type, const char *charset,
+ bool enabled)
{
- return new DilloHtmlForm (html,method,action,enc,charset);
+ return new DilloHtmlForm (html, method, action, content_type, charset,
+ enabled);
}
void a_Html_form_delete (DilloHtmlForm *form)
@@ -216,15 +213,30 @@ void a_Html_input_delete (DilloHtmlInput *input)
delete input;
}
+void a_Html_form_submit2(void *vform)
+{
+ ((DilloHtmlForm *)vform)->submit(NULL, NULL);
+}
+
+void a_Html_form_reset2(void *vform)
+{
+ ((DilloHtmlForm *)vform)->reset();
+}
+
+void a_Html_form_display_hiddens2(void *vform, bool display)
+{
+ ((DilloHtmlForm *)vform)->display_hiddens(display);
+}
+
/*
- * Form parsing functions
+ * Form parsing functions
*/
/*
* Add an HTML control
*/
static void Html_add_input(DilloHtml *html, DilloHtmlInputType type,
- dw::core::ui::Embed *embed, const char *name,
+ Embed *embed, const char *name,
const char *init_str, bool init_val)
{
_MSG("name=[%s] init_str=[%s] init_val=[%d]\n", name, init_str, init_val);
@@ -236,6 +248,8 @@ static void Html_add_input(DilloHtml *html, DilloHtmlInputType type,
int ni = html->inputs_outside_form->size();
html->inputs_outside_form->increase();
html->inputs_outside_form->set(ni, input);
+
+ input->setEnabled(false);
}
}
@@ -244,18 +258,20 @@ static void Html_add_input(DilloHtml *html, DilloHtmlInputType type,
*/
static DilloHtmlInput *Html_get_radio_input(DilloHtml *html, const char *name)
{
- lout::misc::SimpleVector<DilloHtmlInput*>* inputs;
+ if (name) {
+ lout::misc::SimpleVector<DilloHtmlInput*>* inputs;
- if (html->InFlags & IN_FORM)
- inputs = html->getCurrentForm()->inputs;
- else
- inputs = html->inputs_outside_form;
+ if (html->InFlags & IN_FORM)
+ inputs = html->getCurrentForm()->inputs;
+ else
+ inputs = html->inputs_outside_form;
- for (int idx = 0; idx < inputs->size(); idx++) {
- DilloHtmlInput *input = inputs->get(idx);
- if (input->type == DILLO_HTML_INPUT_RADIO &&
- input->name && !dStrcasecmp(input->name, name))
- return input;
+ for (int idx = 0; idx < inputs->size(); idx++) {
+ DilloHtmlInput *input = inputs->get(idx);
+ if (input->type == DILLO_HTML_INPUT_RADIO &&
+ input->name && !dStrcasecmp(input->name, name))
+ return input;
+ }
}
return NULL;
}
@@ -283,11 +299,11 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize)
{
DilloUrl *action;
DilloHtmlMethod method;
- DilloHtmlEnc enc;
+ DilloHtmlEnc content_type;
char *charset, *first;
const char *attrbuf;
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
if (html->InFlags & IN_FORM) {
BUG_MSG("nested forms\n");
@@ -308,13 +324,15 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize)
}
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "action")))
action = a_Html_url_new(html, attrbuf, NULL, 0);
- else
+ else {
+ BUG_MSG("action attribute required for <form>\n");
action = a_Url_dup(html->base_url);
- enc = DILLO_HTML_ENC_URLENCODING;
+ }
+ content_type = DILLO_HTML_ENC_URLENCODED;
if ((method == DILLO_HTML_METHOD_POST) &&
((attrbuf = a_Html_get_attr(html, tag, tagsize, "enctype")))) {
if (!dStrcasecmp(attrbuf, "multipart/form-data"))
- enc = DILLO_HTML_ENC_MULTIPART;
+ content_type = DILLO_HTML_ENC_MULTIPART;
}
charset = NULL;
first = NULL;
@@ -335,32 +353,19 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize)
}
if (!charset)
charset = html->charset;
- html->formNew(method, action, enc, charset);
+ html->formNew(method, action, content_type, charset);
dFree(first);
a_Url_free(action);
}
void Html_tag_close_form(DilloHtml *html, int TagIdx)
{
- static const char *SubmitTag =
- "<input type='submit' value='?Submit?' alt='dillo-generated-button'>";
- DilloHtmlForm *form;
+// DilloHtmlForm *form;
// int i;
-
- if (html->InFlags & IN_FORM) {
- form = html->getCurrentForm ();
- /* If we don't have a submit button and the user desires one,
- let's add a custom one */
- if (form->num_submit_buttons == 0) {
- if (prefs.show_extra_warnings || form->num_entry_fields != 1)
- BUG_MSG("FORM lacks a Submit button\n");
- if (prefs.generate_submit) {
- BUG_MSG(" (added a submit button internally)\n");
- Html_tag_open_input(html, SubmitTag, strlen(SubmitTag));
- form->num_submit_buttons = 0;
- }
- }
-
+//
+// if (html->InFlags & IN_FORM) {
+// form = html->getCurrentForm ();
+//
// /* Make buttons sensitive again */
// for (i = 0; i < form->inputs->size(); i++) {
// input_i = form->inputs->get(i);
@@ -376,14 +381,31 @@ void Html_tag_close_form(DilloHtml *html, int TagIdx)
// a_Dw_button_set_sensitive(DW_BUTTON(input_i->widget), TRUE);
// }
// }
- }
+// }
html->InFlags &= ~IN_FORM;
html->InFlags &= ~IN_SELECT;
html->InFlags &= ~IN_OPTION;
html->InFlags &= ~IN_TEXTAREA;
+}
- a_Html_pop_tag(html, TagIdx);
+/*
+ * get size, restrict it to reasonable value
+ */
+static int Html_input_get_size(DilloHtml *html, const char *attrbuf)
+{
+ const int MAX_SIZE = 1024;
+ int size = 20;
+
+ if (attrbuf) {
+ size = strtol(attrbuf, NULL, 10);
+ if (size < 1 || size > MAX_SIZE) {
+ int badSize = size;
+ size = (size < 1 ? 20 : MAX_SIZE);
+ BUG_MSG("input size=%d, using size=%d instead\n", badSize, size);
+ }
+ }
+ return size;
}
/*
@@ -392,11 +414,13 @@ void Html_tag_close_form(DilloHtml *html, int TagIdx)
void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
{
DilloHtmlInputType inp_type;
- dw::core::ui::Embed *embed = NULL;
+ Resource *resource = NULL;
+ Embed *embed = NULL;
char *value, *name, *type, *init_str;
const char *attrbuf, *label;
bool init_val = false;
-
+ ResourceFactory *factory;
+
if (html->InFlags & IN_SELECT) {
BUG_MSG("<input> element inside <select>\n");
return;
@@ -405,59 +429,50 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
BUG_MSG("<input> element inside <button>\n");
return;
}
-
+
+ factory = HT2LT(html)->getResourceFactory();
+
/* Get 'value', 'name' and 'type' */
value = a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
name = a_Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
type = a_Html_get_attr_wdef(html, tag, tagsize, "type", "");
-
+
init_str = NULL;
inp_type = DILLO_HTML_INPUT_UNKNOWN;
if (!dStrcasecmp(type, "password")) {
inp_type = DILLO_HTML_INPUT_PASSWORD;
- dw::core::ui::EntryResource *entryResource =
- HT2LT(html)->getResourceFactory()->createEntryResource (10, true);
- embed = new dw::core::ui::Embed (entryResource);
- init_str = (value) ? value : NULL;
+ attrbuf = a_Html_get_attr(html, tag, tagsize, "size");
+ int size = Html_input_get_size(html, attrbuf);
+ resource = factory->createEntryResource (size, true, NULL);
+ init_str = value;
} else if (!dStrcasecmp(type, "checkbox")) {
inp_type = DILLO_HTML_INPUT_CHECKBOX;
- dw::core::ui::CheckButtonResource *check_b_r =
- HT2LT(html)->getResourceFactory()->createCheckButtonResource(false);
- embed = new dw::core::ui::Embed (check_b_r);
+ resource = factory->createCheckButtonResource(false);
init_val = (a_Html_get_attr(html, tag, tagsize, "checked") != NULL);
init_str = (value) ? value : dStrdup("on");
} else if (!dStrcasecmp(type, "radio")) {
inp_type = DILLO_HTML_INPUT_RADIO;
- dw::core::ui::RadioButtonResource *rb_r = NULL;
+ RadioButtonResource *rb_r = NULL;
DilloHtmlInput *input = Html_get_radio_input(html, name);
if (input)
- rb_r =
- (dw::core::ui::RadioButtonResource*)
- input->embed->getResource();
- rb_r = HT2LT(html)->getResourceFactory()
- ->createRadioButtonResource(rb_r, false);
- embed = new dw::core::ui::Embed (rb_r);
+ rb_r = (RadioButtonResource*) input->embed->getResource();
+ resource = factory->createRadioButtonResource(rb_r, false);
init_val = (a_Html_get_attr(html, tag, tagsize, "checked") != NULL);
- init_str = (value) ? value : NULL;
+ init_str = value;
} else if (!dStrcasecmp(type, "hidden")) {
inp_type = DILLO_HTML_INPUT_HIDDEN;
- if (value)
- init_str = dStrdup(a_Html_get_attr(html, tag, tagsize, "value"));
+ init_str = value;
+ int size = Html_input_get_size(html, NULL);
+ resource = factory->createEntryResource(size, false, name);
} else if (!dStrcasecmp(type, "submit")) {
inp_type = DILLO_HTML_INPUT_SUBMIT;
init_str = (value) ? value : dStrdup("submit");
- dw::core::ui::LabelButtonResource *label_b_r =
- HT2LT(html)->getResourceFactory()
- ->createLabelButtonResource(init_str);
- embed = new dw::core::ui::Embed (label_b_r);
+ resource = factory->createLabelButtonResource(init_str);
// gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
} else if (!dStrcasecmp(type, "reset")) {
inp_type = DILLO_HTML_INPUT_RESET;
init_str = (value) ? value : dStrdup("Reset");
- dw::core::ui::LabelButtonResource *label_b_r =
- HT2LT(html)->getResourceFactory()
- ->createLabelButtonResource(init_str);
- embed = new dw::core::ui::Embed (label_b_r);
+ resource = factory->createLabelButtonResource(init_str);
// gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
} else if (!dStrcasecmp(type, "image")) {
if (URL_FLAGS(html->base_url) & URL_SpamSafe) {
@@ -466,10 +481,7 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
attrbuf = a_Html_get_attr(html, tag, tagsize, "alt");
label = attrbuf ? attrbuf : value ? value : name ? name : "Submit";
init_str = dStrdup(label);
- dw::core::ui::LabelButtonResource *label_b_r =
- HT2LT(html)->getResourceFactory()
- ->createLabelButtonResource(init_str);
- embed = new dw::core::ui::Embed (label_b_r);
+ resource = factory->createLabelButtonResource(init_str);
// gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
} else {
inp_type = DILLO_HTML_INPUT_IMAGE;
@@ -485,7 +497,7 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
valid = false;
BUG_MSG("Forms with file input MUST use HTTP POST method\n");
MSG("File input ignored in form not using HTTP POST method\n");
- } else if (form->enc != DILLO_HTML_ENC_MULTIPART) {
+ } else if (form->content_type != DILLO_HTML_ENC_MULTIPART) {
valid = false;
BUG_MSG("Forms with file input MUST use multipart/form-data"
" encoding\n");
@@ -496,65 +508,60 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
if (valid) {
inp_type = DILLO_HTML_INPUT_FILE;
init_str = dStrdup("File selector");
- dw::core::ui::LabelButtonResource *lbr =
- HT2LT(html)->getResourceFactory()->
- createLabelButtonResource(init_str);
- embed = new dw::core::ui::Embed (lbr);
+ resource = factory->createLabelButtonResource(init_str);
}
} else if (!dStrcasecmp(type, "button")) {
inp_type = DILLO_HTML_INPUT_BUTTON;
if (value) {
init_str = value;
- dw::core::ui::LabelButtonResource *label_b_r =
- HT2LT(html)->getResourceFactory()
- ->createLabelButtonResource(init_str);
- embed = new dw::core::ui::Embed (label_b_r);
+ resource = factory->createLabelButtonResource(init_str);
}
} else if (!dStrcasecmp(type, "text") || !*type) {
/* Text input, which also is the default */
inp_type = DILLO_HTML_INPUT_TEXT;
- dw::core::ui::EntryResource *entryResource =
- HT2LT(html)->getResourceFactory()->createEntryResource (10, false);
- embed = new dw::core::ui::Embed (entryResource);
- init_str = (value) ? value : NULL;
+ attrbuf = a_Html_get_attr(html, tag, tagsize, "size");
+ int size = Html_input_get_size(html, attrbuf);
+ resource = factory->createEntryResource(size, false, NULL);
+ init_str = value;
} else {
/* Unknown input type */
BUG_MSG("Unknown input type: \"%s\"\n", type);
}
+ if (resource)
+ embed = new Embed (resource);
+
if (inp_type != DILLO_HTML_INPUT_UNKNOWN) {
Html_add_input(html, inp_type, embed, name,
(init_str) ? init_str : "", init_val);
}
-
- if (embed != NULL && inp_type != DILLO_HTML_INPUT_IMAGE &&
+
+ if (embed != NULL && inp_type != DILLO_HTML_INPUT_IMAGE &&
inp_type != DILLO_HTML_INPUT_UNKNOWN) {
+ if (inp_type == DILLO_HTML_INPUT_HIDDEN) {
+ /* TODO Perhaps do this with access to current form setting */
+ embed->setDisplayed(false);
+ }
if (inp_type == DILLO_HTML_INPUT_TEXT ||
inp_type == DILLO_HTML_INPUT_PASSWORD) {
- dw::core::ui::EntryResource *entryres =
- (dw::core::ui::EntryResource*)embed->getResource();
- /* Readonly or not? */
if (a_Html_get_attr(html, tag, tagsize, "readonly"))
- entryres->setEditable(false);
+ ((EntryResource *) resource)->setEditable(false);
-// /* Set width of the entry */
-// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size")))
-// gtk_widget_set_usize(widget, (strtol(attrbuf, NULL, 10) + 1) *
-// gdk_char_width(widget->style->font, '0'), 0);
-//
// /* Maximum length of the text in the entry */
// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "maxlength")))
// gtk_entry_set_max_length(GTK_ENTRY(widget),
// strtol(attrbuf, NULL, 10));
}
- Color *bg;
- if (prefs.standard_widget_colors)
- bg = NULL;
- else
- bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color);
- HTML_SET_TOP_ATTR(html, backgroundColor, bg);
-
- DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style);
+ if (prefs.show_tooltip &&
+ (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
+ CssPropertyList props;
+ char *tooltip_str = dStrdup(attrbuf);
+
+ props.set (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, tooltip_str);
+ html->styleEngine->setNonCssHints (&props);
+ dFree(tooltip_str);
+ }
+ HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle());
}
dFree(type);
dFree(name);
@@ -570,7 +577,7 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize)
{
DilloUrl *action;
- dw::core::ui::Embed *embed;
+ Embed *embed;
const char *attrbuf;
if (html->InFlags & IN_FORM) {
@@ -582,28 +589,22 @@ void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize)
action = a_Html_url_new(html, attrbuf, NULL, 0);
else
action = a_Url_dup(html->base_url);
-
- html->formNew(DILLO_HTML_METHOD_GET, action, DILLO_HTML_ENC_URLENCODING,
+
+ html->formNew(DILLO_HTML_METHOD_GET, action, DILLO_HTML_ENC_URLENCODED,
html->charset);
html->InFlags |= IN_FORM;
-
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
-
+
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "prompt")))
- DW2TB(html->dw)->addText(attrbuf, S_TOP(html)->style);
-
- dw::core::ui::EntryResource *entryResource =
- HT2LT(html)->getResourceFactory()->createEntryResource (10, false);
- embed = new dw::core::ui::Embed (entryResource);
+ HT2TB(html)->addText(attrbuf, html->styleEngine->wordStyle ());
+
+ ResourceFactory *factory = HT2LT(html)->getResourceFactory();
+ EntryResource *entryResource = factory->createEntryResource (20,false,NULL);
+ embed = new Embed (entryResource);
Html_add_input(html, DILLO_HTML_INPUT_INDEX, embed, NULL, NULL, FALSE);
- Color *bg;
- if (prefs.standard_widget_colors)
- bg = NULL;
- else
- bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color);
- HTML_SET_TOP_ATTR(html, backgroundColor, bg);
- DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style);
+ HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
a_Url_free(action);
html->InFlags &= ~IN_FORM;
@@ -620,7 +621,7 @@ void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize)
char *name;
const char *attrbuf;
int cols, rows;
-
+
if (html->InFlags & IN_TEXTAREA) {
BUG_MSG("nested <textarea>\n");
html->ReqTagClose = TRUE;
@@ -630,22 +631,28 @@ void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize)
BUG_MSG("<textarea> element inside <select>\n");
return;
}
-
+
html->InFlags |= IN_TEXTAREA;
a_Html_stash_init(html);
S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
-
- cols = 20;
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cols")))
+
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cols"))) {
cols = strtol(attrbuf, NULL, 10);
+ } else {
+ BUG_MSG("cols attribute is required for <textarea>\n");
+ cols = 20;
+ }
if (cols < 1 || cols > MAX_COLS) {
int badCols = cols;
cols = (cols < 1 ? 20 : MAX_COLS);
BUG_MSG("textarea cols=%d, using cols=%d instead\n", badCols, cols);
}
- rows = 10;
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rows")))
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rows"))) {
rows = strtol(attrbuf, NULL, 10);
+ } else {
+ BUG_MSG("rows attribute is required for <textarea>\n");
+ rows = 10;
+ }
if (rows < 1 || rows > MAX_ROWS) {
int badRows = rows;
rows = (rows < 1 ? 2 : MAX_ROWS);
@@ -655,24 +662,17 @@ void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize)
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name")))
name = dStrdup(attrbuf);
- dw::core::ui::MultiLineTextResource *textres =
- HT2LT(html)->getResourceFactory()->createMultiLineTextResource (cols,
- rows);
+ ResourceFactory *factory = HT2LT(html)->getResourceFactory();
+ MultiLineTextResource *textres =
+ factory->createMultiLineTextResource (cols, rows);
- dw::core::ui::Embed *embed = new dw::core::ui::Embed(textres);
+ Embed *embed = new Embed(textres);
/* Readonly or not? */
if (a_Html_get_attr(html, tag, tagsize, "readonly"))
textres->setEditable(false);
Html_add_input(html, DILLO_HTML_INPUT_TEXTAREA, embed, name, NULL, false);
- Color *bg;
- if (prefs.standard_widget_colors)
- bg = NULL;
- else
- bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color);
- HTML_SET_TOP_ATTR(html, backgroundColor, bg);
-
- DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style);
+ HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
dFree(name);
}
@@ -692,7 +692,7 @@ void Html_tag_close_textarea(DilloHtml *html, int TagIdx)
dStr_erase(html->Stash, 0, 1);
if (html->Stash->str[0] == '\n')
dStr_erase(html->Stash, 0, 1);
-
+
/* As the spec recommends to canonicalize line endings, it is safe
* to replace '\r' with '\n'. It will be canonicalized anyway! */
for (i = 0; i < html->Stash->len; ++i) {
@@ -703,17 +703,15 @@ void Html_tag_close_textarea(DilloHtml *html, int TagIdx)
html->Stash->str[i] = '\n';
}
}
-
+
/* The HTML3.2 spec says it can have "text and character entities". */
str = a_Html_parse_entities(html, html->Stash->str, html->Stash->len);
input = Html_get_current_input(html);
input->init_str = str;
- ((dw::core::ui::MultiLineTextResource *)input->embed->getResource ())
- ->setText(str);
+ ((MultiLineTextResource *)input->embed->getResource ())->setText(str);
html->InFlags &= ~IN_TEXTAREA;
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -722,8 +720,8 @@ void Html_tag_close_textarea(DilloHtml *html, int TagIdx)
/* The select tag is quite tricky, because of gorpy html syntax. */
void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize)
{
-// const char *attrbuf;
-// int size, type, multi;
+ const char *attrbuf;
+ int rows = 0;
if (html->InFlags & IN_SELECT) {
BUG_MSG("nested <select>\n");
@@ -733,51 +731,32 @@ void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize)
html->InFlags &= ~IN_OPTION;
char *name = a_Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
- dw::core::ui::ResourceFactory *factory =
- HT2LT(html)->getResourceFactory ();
+ ResourceFactory *factory = HT2LT(html)->getResourceFactory ();
DilloHtmlInputType type;
- dw::core::ui::SelectionResource *res;
- if (a_Html_get_attr(html, tag, tagsize, "multiple")) {
- type = DILLO_HTML_INPUT_SEL_LIST;
- res = factory->createListResource (
- dw::core::ui::ListResource::SELECTION_MULTIPLE);
- } else {
- type = DILLO_HTML_INPUT_SELECT;
- res = factory->createOptionMenuResource ();
+ SelectionResource *res;
+ bool multi = a_Html_get_attr(html, tag, tagsize, "multiple") != NULL;
+
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size"))) {
+ rows = strtol(attrbuf, NULL, 10);
+ if (rows > 100)
+ rows = 100;
}
- dw::core::ui::Embed *embed = new dw::core::ui::Embed(res);
+ if (rows < 1)
+ rows = multi ? 10 : 1;
- int bg;
- if (prefs.standard_widget_colors) {
- /* Valid colors required; SELECT can contain other elements (BUG) */
- HTML_SET_TOP_ATTR(html, color, Color::createSimple (HT2LT(html), 0));
- bg = 0xffffff;
+ if (rows == 1 && multi == false) {
+ type = DILLO_HTML_INPUT_SELECT;
+ res = factory->createOptionMenuResource ();
} else {
- bg = S_TOP(html)->current_bg_color;
+ type = DILLO_HTML_INPUT_SEL_LIST;
+ res = factory->createListResource (multi ?
+ ListResource::SELECTION_MULTIPLE :
+ ListResource::SELECTION_EXACTLY_ONE,
+ rows);
}
- HTML_SET_TOP_ATTR(html, backgroundColor,
- Color::createShaded (HT2LT(html), bg));
- DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style);
+ Embed *embed = new Embed(res);
-// size = 0;
-// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size")))
-// size = strtol(attrbuf, NULL, 10);
-//
-// multi = (a_Html_get_attr(html, tag, tagsize, "multiple")) ? 1 : 0;
-// if (size < 1)
-// size = multi ? 10 : 1;
-//
-// if (size == 1) {
-// menu = gtk_menu_new();
-// widget = gtk_option_menu_new();
-// type = DILLO_HTML_INPUT_SELECT;
-// } else {
-// menu = gtk_list_new();
-// widget = menu;
-// if (multi)
-// gtk_list_set_selection_mode(GTK_LIST(menu), GTK_SELECTION_MULTIPLE);
-// type = DILLO_HTML_INPUT_SEL_LIST;
-// }
+ HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
Html_add_input(html, type, embed, name, NULL, false);
a_Html_stash_init(html);
@@ -798,15 +777,12 @@ void Html_tag_close_select(DilloHtml *html, int TagIdx)
DilloHtmlInput *input = Html_get_current_input(html);
DilloHtmlSelect *select = input->select;
- // BUG(?): should not do this for MULTI selections
+ // BUG(?): should not do this for MULTI selections
select->ensureSelection ();
- dw::core::ui::SelectionResource *res =
- (dw::core::ui::SelectionResource*)input->embed->getResource();
+ SelectionResource *res = (SelectionResource*)input->embed->getResource();
select->addOptionsTo (res);
}
-
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -826,13 +802,10 @@ void Html_tag_open_option(DilloHtml *html, const char *tag, int tagsize)
if (input->type == DILLO_HTML_INPUT_SELECT ||
input->type == DILLO_HTML_INPUT_SEL_LIST) {
- char *value =
- a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
- bool selected =
- (a_Html_get_attr(html, tag, tagsize, "selected") != NULL);
- bool enabled =
- (a_Html_get_attr(html, tag, tagsize, "disabled") == NULL);
- input->select->addOption(value,selected,enabled);
+ char *value = a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
+ bool selected = (a_Html_get_attr(html, tag, tagsize,"selected") != NULL);
+ bool enabled = (a_Html_get_attr(html, tag, tagsize, "disabled") == NULL);
+ input->select->addOption(value, selected, enabled);
}
a_Html_stash_init(html);
@@ -876,37 +849,23 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize)
if (inp_type != DILLO_HTML_INPUT_UNKNOWN) {
/* Render the button */
- dw::core::style::StyleAttrs style_attrs;
- dw::core::style::Style *style;
- dw::core::Widget *page;
- dw::core::ui::Embed *embed;
+ Widget *page;
+ Embed *embed;
char *name, *value;
- style_attrs = *S_TOP(html)->style;
- style_attrs.margin.setVal(0);
- style_attrs.borderWidth.setVal(0);
- style_attrs.padding.setVal(0);
- style_attrs.backgroundColor =
- Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color);
- style = Style::create (HT2LT(html), &style_attrs);
-
page = new Textblock (prefs.limit_text_width);
- page->setStyle (style);
+ page->setStyle (html->styleEngine->backgroundStyle ());
- dw::core::ui::ComplexButtonResource *complex_b_r =
- HT2LT(html)->getResourceFactory()
- ->createComplexButtonResource(page, true);
- embed = new dw::core::ui::Embed(complex_b_r);
+ ResourceFactory *factory = HT2LT(html)->getResourceFactory();
+ Resource *resource = factory->createComplexButtonResource(page, true);
+ embed = new Embed(resource);
// a_Dw_button_set_sensitive (DW_BUTTON (button), FALSE);
- DW2TB(html->dw)->addParbreak (5, style);
- DW2TB(html->dw)->addWidget (embed, style);
- DW2TB(html->dw)->addParbreak (5, style);
- style->unref ();
+ HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
+ HT2TB(html)->addWidget (embed, html->styleEngine->style ());
+ HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
S_TOP(html)->textblock = html->dw = page;
- /* right button press for menus for button contents */
- html->connectSignals(page);
value = a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
name = a_Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
@@ -924,39 +883,39 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize)
void Html_tag_close_button(DilloHtml *html, int TagIdx)
{
html->InFlags &= ~IN_BUTTON;
- a_Html_pop_tag(html, TagIdx);
}
/*
- * Class implementations
+ * Class implementations
*/
/*
- * DilloHtmlForm
+ * DilloHtmlForm
*/
/*
- * Constructor
+ * Constructor
*/
DilloHtmlForm::DilloHtmlForm (DilloHtml *html2,
DilloHtmlMethod method2,
const DilloUrl *action2,
- DilloHtmlEnc enc2,
- const char *charset)
+ DilloHtmlEnc content_type2,
+ const char *charset, bool enabled)
{
html = html2;
method = method2;
action = a_Url_dup(action2);
- enc = enc2;
+ content_type = content_type2;
submit_charset = dStrdup(charset);
inputs = new misc::SimpleVector <DilloHtmlInput*> (4);
num_entry_fields = 0;
- num_submit_buttons = 0;
+ showing_hiddens = false;
+ this->enabled = enabled;
form_receiver = new DilloHtmlReceiver (this);
}
/*
- * Destructor
+ * Destructor
*/
DilloHtmlForm::~DilloHtmlForm ()
{
@@ -969,17 +928,18 @@ DilloHtmlForm::~DilloHtmlForm ()
delete(form_receiver);
}
-void DilloHtmlForm::eventHandler(dw::core::ui::Resource *resource)
+void DilloHtmlForm::eventHandler(Resource *resource, EventButton *event)
{
MSG("DilloHtmlForm::eventHandler\n");
- DilloHtmlInput *input = getInput(resource);
- if (input) {
- bool force_submit =
- prefs.enterpress_forces_submit ||
- num_entry_fields == 1;
- input->activate (this, force_submit);
+ if (event && (event->button == 3)) {
+ a_UIcmd_form_popup(html->bw, html->page_url, this, showing_hiddens);
} else {
- MSG("DilloHtmlForm::eventHandler: ERROR, input not found!\n");
+ DilloHtmlInput *input = getInput(resource);
+ if (input) {
+ input->activate (this, num_entry_fields, event);
+ } else {
+ MSG("DilloHtmlForm::eventHandler: ERROR, input not found!\n");
+ }
}
}
@@ -987,11 +947,21 @@ void DilloHtmlForm::eventHandler(dw::core::ui::Resource *resource)
* Submit.
* (Called by eventHandler())
*/
-void DilloHtmlForm::submit(DilloHtmlInput *input)
+void DilloHtmlForm::submit(DilloHtmlInput *active_input, EventButton *event)
{
- DilloUrl *url = buildQueryUrl(input);
+ DilloUrl *url = buildQueryUrl(active_input);
if (url) {
- a_Nav_push(html->bw, url);
+ if (event && event->button == 2) {
+ if (prefs.middle_click_opens_new_tab) {
+ int focus = prefs.focus_new_tab ? 1 : 0;
+ if (event->state == SHIFT_MASK) focus = !focus;
+ a_UIcmd_open_url_nt(html->bw, url, focus);
+ } else {
+ a_UIcmd_open_url_nw(html->bw, url);
+ }
+ } else {
+ a_UIcmd_open_url(html->bw, url);
+ }
a_Url_free(url);
}
// /* now, make the rendered area have its focus back */
@@ -1002,7 +972,7 @@ void DilloHtmlForm::submit(DilloHtmlInput *input)
* Build a new query URL.
* (Called by submit())
*/
-DilloUrl *DilloHtmlForm::buildQueryUrl(DilloHtmlInput *input)
+DilloUrl *DilloHtmlForm::buildQueryUrl(DilloHtmlInput *active_input)
{
DilloUrl *new_url = NULL;
@@ -1013,11 +983,11 @@ DilloUrl *DilloHtmlForm::buildQueryUrl(DilloHtmlInput *input)
_MSG("DilloHtmlForm::buildQueryUrl: action=%s\n",URL_STR_(action));
- if (num_submit_buttons > 0) {
- if ((input->type == DILLO_HTML_INPUT_SUBMIT) ||
- (input->type == DILLO_HTML_INPUT_IMAGE) ||
- (input->type == DILLO_HTML_INPUT_BUTTON_SUBMIT)) {
- active_submit = input;
+ if (active_input) {
+ if ((active_input->type == DILLO_HTML_INPUT_SUBMIT) ||
+ (active_input->type == DILLO_HTML_INPUT_IMAGE) ||
+ (active_input->type == DILLO_HTML_INPUT_BUTTON_SUBMIT)) {
+ active_submit = active_input;
}
}
@@ -1031,7 +1001,7 @@ DilloUrl *DilloHtmlForm::buildQueryUrl(DilloHtmlInput *input)
/* new_url keeps the dStr and sets DataStr to NULL */
a_Url_set_data(new_url, &DataStr);
a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_Post);
- if (enc == DILLO_HTML_ENC_MULTIPART)
+ if (content_type == DILLO_HTML_ENC_MULTIPART)
a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_MultipartEnc);
} else {
/* remove <fragment> and <query> sections if present */
@@ -1063,11 +1033,11 @@ Dstr *DilloHtmlForm::buildQueryData(DilloHtmlInput *active_submit)
{
Dstr *DataStr = NULL;
char *boundary = NULL;
- iconv_t encoder = (iconv_t) -1;
+ iconv_t char_encoder = (iconv_t) -1;
if (submit_charset && dStrcasecmp(submit_charset, "UTF-8")) {
- encoder = iconv_open(submit_charset, "UTF-8");
- if (encoder == (iconv_t) -1) {
+ char_encoder = iconv_open(submit_charset, "UTF-8");
+ if (char_encoder == (iconv_t) -1) {
MSG_WARN("Cannot convert to character encoding '%s'\n",
submit_charset);
} else {
@@ -1075,95 +1045,95 @@ Dstr *DilloHtmlForm::buildQueryData(DilloHtmlInput *active_submit)
}
}
- if (enc == DILLO_HTML_ENC_MULTIPART) {
- if (!(boundary = makeMultipartBoundary(encoder, active_submit)))
+ if (content_type == DILLO_HTML_ENC_MULTIPART) {
+ if (!(boundary = makeMultipartBoundary(char_encoder, active_submit)))
MSG_ERR("Cannot generate multipart/form-data boundary.\n");
}
- if ((enc == DILLO_HTML_ENC_URLENCODING) || (boundary != NULL)) {
+ if ((content_type == DILLO_HTML_ENC_URLENCODED) || (boundary != NULL)) {
Dlist *values = dList_new(5);
DataStr = dStr_sized_new(4096);
- for (int input_idx = 0; input_idx < inputs->size(); input_idx++) {
- DilloHtmlInput *input = inputs->get (input_idx);
+ for (int i = 0; i < inputs->size(); i++) {
+ DilloHtmlInput *input = inputs->get (i);
Dstr *name = dStr_new(input->name);
bool is_active_submit = (input == active_submit);
+ int valcount;
- name = encodeText(encoder, &name);
+ name = encodeText(char_encoder, &name);
input->appendValuesTo(values, is_active_submit);
- if (input->type == DILLO_HTML_INPUT_FILE && dList_length(values) >0) {
- if (dList_length(values) > 1)
- MSG_WARN("multiple files per form control not supported\n");
- Dstr *file = (Dstr *) dList_nth_data(values, 0);
- dList_remove(values, file);
-
- /* Get filename and encode it. Do not encode file contents. */
- dw::core::ui::LabelButtonResource *lbr =
- (dw::core::ui::LabelButtonResource*)
- input->embed->getResource();
- const char *filename = lbr->getLabel();
- if (filename[0] && strcmp(filename, input->init_str)) {
- char *p = strrchr(filename, '/');
- if (p)
- filename = p + 1; /* don't reveal path */
- Dstr *dfilename = dStr_new(filename);
- dfilename = encodeText(encoder, &dfilename);
- appendInputMultipartFiles(DataStr, boundary, name->str,
- file, dfilename->str);
- dStr_free(dfilename, 1);
- }
- dStr_free(file, 1);
- } else if (input->type == DILLO_HTML_INPUT_INDEX) {
- Dstr *val = (Dstr *) dList_nth_data(values, 0);
- dList_remove(values, val);
- val = encodeText(encoder, &val);
- urlencodeAppend(DataStr, val->str);
- dStr_free(val, 1);
- } else if (input->type == DILLO_HTML_INPUT_IMAGE) {
- if (dList_length(values) > 0) {
+ if ((valcount = dList_length(values)) > 0) {
+ if (input->type == DILLO_HTML_INPUT_FILE) {
+ if (valcount > 1)
+ MSG_WARN("multiple files per form control not supported\n");
+ Dstr *file = (Dstr *) dList_nth_data(values, 0);
+ dList_remove(values, file);
+
+ /* Get filename and encode it. Do not encode file contents. */
+ LabelButtonResource *lbr =
+ (LabelButtonResource*) input->embed->getResource();
+ const char *filename = lbr->getLabel();
+ if (filename[0] && strcmp(filename, input->init_str)) {
+ const char *p = strrchr(filename, '/');
+ if (p)
+ filename = p + 1; /* don't reveal path */
+ Dstr *dfilename = dStr_new(filename);
+ dfilename = encodeText(char_encoder, &dfilename);
+ filesInputMultipartAppend(DataStr, boundary, name->str,
+ file, dfilename->str);
+ dStr_free(dfilename, 1);
+ }
+ dStr_free(file, 1);
+ } else if (input->type == DILLO_HTML_INPUT_INDEX) {
+ /* no name */
+ Dstr *val = (Dstr *) dList_nth_data(values, 0);
+ dList_remove(values, val);
+ val = encodeText(char_encoder, &val);
+ strUrlencodeAppend(DataStr, val->str);
+ dStr_free(val, 1);
+ } else if (input->type == DILLO_HTML_INPUT_IMAGE) {
Dstr *x, *y;
x = (Dstr *) dList_nth_data(values, 0);
dList_remove(values, x);
y = (Dstr *) dList_nth_data(values, 0);
dList_remove(values, y);
- if (enc == DILLO_HTML_ENC_URLENCODING)
- appendClickposUrlencode(DataStr, name, x, y);
- else if (enc == DILLO_HTML_ENC_MULTIPART)
- appendClickposMultipart(DataStr, boundary, name, x, y);
+ if (content_type == DILLO_HTML_ENC_URLENCODED)
+ imageInputUrlencodeAppend(DataStr, name, x, y);
+ else if (content_type == DILLO_HTML_ENC_MULTIPART)
+ imageInputMultipartAppend(DataStr, boundary, name, x, y);
dStr_free(x, 1);
dStr_free(y, 1);
- }
- } else {
- int length = dList_length(values), i;
- for (i = 0; i < length; i++) {
- Dstr *val = (Dstr *) dList_nth_data(values, 0);
- dList_remove(values, val);
- val = encodeText(encoder, &val);
- if (enc == DILLO_HTML_ENC_URLENCODING)
- appendInputUrlencode(DataStr, name->str, val->str);
- else if (enc == DILLO_HTML_ENC_MULTIPART)
- appendInputMultipart(DataStr, boundary,
- name->str, val->str);
- dStr_free(val, 1);
+ } else {
+ for (int j = 0; j < valcount; j++) {
+ Dstr *val = (Dstr *) dList_nth_data(values, 0);
+ dList_remove(values, val);
+ val = encodeText(char_encoder, &val);
+ if (content_type == DILLO_HTML_ENC_URLENCODED)
+ inputUrlencodeAppend(DataStr, name->str, val->str);
+ else if (content_type == DILLO_HTML_ENC_MULTIPART)
+ inputMultipartAppend(DataStr, boundary, name->str,
+ val->str);
+ dStr_free(val, 1);
+ }
}
}
dStr_free(name, 1);
}
if (DataStr->len > 0) {
- if (enc == DILLO_HTML_ENC_URLENCODING) {
+ if (content_type == DILLO_HTML_ENC_URLENCODED) {
if (DataStr->str[DataStr->len - 1] == '&')
dStr_truncate(DataStr, DataStr->len - 1);
- } else if (enc == DILLO_HTML_ENC_MULTIPART) {
+ } else if (content_type == DILLO_HTML_ENC_MULTIPART) {
dStr_append(DataStr, "--");
}
}
dList_free(values);
}
dFree(boundary);
- if (encoder != (iconv_t) -1)
- (void)iconv_close(encoder);
+ if (char_encoder != (iconv_t) -1)
+ (void)iconv_close(char_encoder);
return DataStr;
}
@@ -1171,7 +1141,7 @@ Dstr *DilloHtmlForm::buildQueryData(DilloHtmlInput *active_submit)
* Generate a boundary string for use in separating the parts of a
* multipart/form-data submission.
*/
-char *DilloHtmlForm::makeMultipartBoundary(iconv_t encoder,
+char *DilloHtmlForm::makeMultipartBoundary(iconv_t char_encoder,
DilloHtmlInput *active_submit)
{
const int max_tries = 10;
@@ -1181,25 +1151,25 @@ char *DilloHtmlForm::makeMultipartBoundary(iconv_t encoder,
char *ret = NULL;
/* fill DataStr with names, filenames, and values */
- for (int input_idx = 0; input_idx < inputs->size(); input_idx++) {
+ for (int i = 0; i < inputs->size(); i++) {
Dstr *dstr;
- DilloHtmlInput *input = inputs->get (input_idx);
+ DilloHtmlInput *input = inputs->get (i);
bool is_active_submit = (input == active_submit);
input->appendValuesTo(values, is_active_submit);
if (input->name) {
dstr = dStr_new(input->name);
- dstr = encodeText(encoder, &dstr);
+ dstr = encodeText(char_encoder, &dstr);
dStr_append_l(DataStr, dstr->str, dstr->len);
dStr_free(dstr, 1);
}
if (input->type == DILLO_HTML_INPUT_FILE) {
- dw::core::ui::LabelButtonResource *lbr =
- (dw::core::ui::LabelButtonResource*)input->embed->getResource();
+ LabelButtonResource *lbr =
+ (LabelButtonResource*)input->embed->getResource();
const char *filename = lbr->getLabel();
if (filename[0] && strcmp(filename, input->init_str)) {
dstr = dStr_new(filename);
- dstr = encodeText(encoder, &dstr);
+ dstr = encodeText(char_encoder, &dstr);
dStr_append_l(DataStr, dstr->str, dstr->len);
dStr_free(dstr, 1);
}
@@ -1209,7 +1179,7 @@ char *DilloHtmlForm::makeMultipartBoundary(iconv_t encoder,
dstr = (Dstr *) dList_nth_data(values, 0);
dList_remove(values, dstr);
if (input->type != DILLO_HTML_INPUT_FILE)
- dstr = encodeText(encoder, &dstr);
+ dstr = encodeText(char_encoder, &dstr);
dStr_append_l(DataStr, dstr->str, dstr->len);
dStr_free(dstr, 1);
}
@@ -1233,9 +1203,9 @@ char *DilloHtmlForm::makeMultipartBoundary(iconv_t encoder,
/*
* Pass input text through character set encoder.
* Return value: same input Dstr if no encoding is needed.
- new Dstr when encoding (input Dstr is freed).
+ * new Dstr when encoding (input Dstr is freed).
*/
-Dstr *DilloHtmlForm::encodeText(iconv_t encoder, Dstr **input)
+Dstr *DilloHtmlForm::encodeText(iconv_t char_encoder, Dstr **input)
{
int rc = 0;
Dstr *output;
@@ -1245,7 +1215,7 @@ Dstr *DilloHtmlForm::encodeText(iconv_t encoder, Dstr **input)
size_t inLeft, outRoom;
bool bad_chars = false;
- if ((encoder == (iconv_t) -1) || *input == NULL || (*input)->len == 0)
+ if ((char_encoder == (iconv_t) -1) || *input == NULL || (*input)->len == 0)
return *input;
output = dStr_new("");
@@ -1258,7 +1228,7 @@ Dstr *DilloHtmlForm::encodeText(iconv_t encoder, Dstr **input)
outPtr = buffer;
outRoom = bufsize;
- rc = iconv(encoder, &inPtr, &inLeft, &outPtr, &outRoom);
+ rc = iconv(char_encoder, &inPtr, &inLeft, &outPtr, &outRoom);
// iconv() on success, number of bytes converted
// -1, errno == EILSEQ illegal byte sequence found
@@ -1298,28 +1268,27 @@ Dstr *DilloHtmlForm::encodeText(iconv_t encoder, Dstr **input)
return output;
}
-
+
/*
- * Urlencode 'val' and append it to 'str'
+ * Urlencode 'str' and append it to 'dstr'
*/
-void DilloHtmlForm::urlencodeAppend(Dstr *str, const char *val)
+void DilloHtmlForm::strUrlencodeAppend(Dstr *dstr, const char *str)
{
- char *enc_val = a_Url_encode_hex_str(val);
- dStr_append(str, enc_val);
- dFree(enc_val);
+ char *encoded = a_Url_encode_hex_str(str);
+ dStr_append(dstr, encoded);
+ dFree(encoded);
}
/*
* Append a name-value pair to url data using url encoding.
*/
-void DilloHtmlForm::appendInputUrlencode(Dstr *data,
- const char *name,
+void DilloHtmlForm::inputUrlencodeAppend(Dstr *data, const char *name,
const char *value)
{
if (name && name[0]) {
- urlencodeAppend(data, name);
+ strUrlencodeAppend(data, name);
dStr_append_c(data, '=');
- urlencodeAppend(data, value);
+ strUrlencodeAppend(data, value);
dStr_append_c(data, '&');
}
}
@@ -1328,7 +1297,7 @@ void DilloHtmlForm::appendInputUrlencode(Dstr *data,
* Append files to URL data using multipart encoding.
* Currently only accepts one file.
*/
-void DilloHtmlForm::appendInputMultipartFiles(Dstr* data,
+void DilloHtmlForm::filesInputMultipartAppend(Dstr* data,
const char *boundary,
const char *name,
Dstr *file,
@@ -1379,7 +1348,7 @@ void DilloHtmlForm::appendInputMultipartFiles(Dstr* data,
/*
* Append a name-value pair to url data using multipart encoding.
*/
-void DilloHtmlForm::appendInputMultipart(Dstr *data,
+void DilloHtmlForm::inputMultipartAppend(Dstr *data,
const char *boundary,
const char *name,
const char *value)
@@ -1403,13 +1372,13 @@ void DilloHtmlForm::appendInputMultipart(Dstr *data,
/*
* Append an image button click position to url data using url encoding.
*/
-void DilloHtmlForm::appendClickposUrlencode(Dstr *data, Dstr *name, Dstr *x,
- Dstr *y)
+void DilloHtmlForm::imageInputUrlencodeAppend(Dstr *data, Dstr *name, Dstr *x,
+ Dstr *y)
{
if (name->len) {
- urlencodeAppend(data, name->str);
+ strUrlencodeAppend(data, name->str);
dStr_sprintfa(data, ".x=%s&", x->str);
- urlencodeAppend(data, name->str);
+ strUrlencodeAppend(data, name->str);
dStr_sprintfa(data, ".y=%s&", y->str);
} else
dStr_sprintfa(data, "x=%s&y=%s&", x->str, y->str);
@@ -1418,8 +1387,8 @@ void DilloHtmlForm::appendClickposUrlencode(Dstr *data, Dstr *name, Dstr *x,
/*
* Append an image button click position to url data using multipart encoding.
*/
-void DilloHtmlForm::appendClickposMultipart(Dstr *data, const char *boundary,
- Dstr *name, Dstr *x, Dstr *y)
+void DilloHtmlForm::imageInputMultipartAppend(Dstr *data, const char *boundary,
+ Dstr *name, Dstr *x, Dstr *y)
{
int orig_len = name->len;
@@ -1427,10 +1396,10 @@ void DilloHtmlForm::appendClickposMultipart(Dstr *data, const char *boundary,
dStr_append_c(name, '.');
dStr_append_c(name, 'x');
- appendInputMultipart(data, boundary, name->str, x->str);
+ inputMultipartAppend(data, boundary, name->str, x->str);
dStr_truncate(name, name->len - 1);
dStr_append_c(name, 'y');
- appendInputMultipart(data, boundary, name->str, y->str);
+ inputMultipartAppend(data, boundary, name->str, y->str);
dStr_truncate(name, orig_len);
}
@@ -1446,11 +1415,33 @@ void DilloHtmlForm::reset ()
}
/*
+ * Show/hide "hidden" form controls
+ */
+void DilloHtmlForm::display_hiddens(bool display)
+{
+ int size = inputs->size();
+ for (int i = 0; i < size; i++) {
+ DilloHtmlInput *input = inputs->get(i);
+ if (input->type == DILLO_HTML_INPUT_HIDDEN) {
+ input->embed->setDisplayed(display);
+ }
+ }
+ showing_hiddens = display;
+}
+
+void DilloHtmlForm::setEnabled(bool enabled)
+{
+ for (int i = 0; i < inputs->size(); i++)
+ inputs->get(i)->setEnabled(enabled);
+}
+
+/*
* Add a new input.
*/
void DilloHtmlForm::addInput(DilloHtmlInput *input, DilloHtmlInputType type)
{
input->connectTo (form_receiver);
+ input->setEnabled (enabled);
int ni = inputs->size ();
inputs->increase ();
inputs->set (ni,input);
@@ -1459,17 +1450,13 @@ void DilloHtmlForm::addInput(DilloHtmlInput *input, DilloHtmlInputType type)
if (type == DILLO_HTML_INPUT_PASSWORD ||
type == DILLO_HTML_INPUT_TEXT) {
num_entry_fields++;
- } else if (type == DILLO_HTML_INPUT_SUBMIT ||
- type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
- type == DILLO_HTML_INPUT_IMAGE) {
- num_submit_buttons++;
}
}
/*
* Return the input with a given resource.
*/
-DilloHtmlInput *DilloHtmlForm::getInput (dw::core::ui::Resource *resource)
+DilloHtmlInput *DilloHtmlForm::getInput (Resource *resource)
{
for (int idx = 0; idx < inputs->size(); idx++) {
DilloHtmlInput *input = inputs->get(idx);
@@ -1495,22 +1482,22 @@ DilloHtmlInput *DilloHtmlForm::getRadioInput (const char *name)
}
/*
- * DilloHtmlReceiver
+ * DilloHtmlReceiver
*
* TODO: Currently there's "clicked" for buttons, we surely need "enter" for
* textentries, and maybe the "mouseover, ...." set for Javascript.
*/
-void DilloHtmlReceiver::activate (dw::core::ui::Resource *resource)
+void DilloHtmlReceiver::activate (Resource *resource)
{
- form->eventHandler(resource);
+ form->eventHandler(resource, NULL);
}
/*
* Enter a form control, as in "onmouseover".
* For _pressing_ enter in a text control, see activate().
*/
-void DilloHtmlReceiver::enter (dw::core::ui::Resource *resource)
+void DilloHtmlReceiver::enter (Resource *resource)
{
DilloHtml *html = form->html;
DilloHtmlInput *input = form->getInput(resource);
@@ -1532,29 +1519,27 @@ void DilloHtmlReceiver::enter (dw::core::ui::Resource *resource)
/*
* Leave a form control, or "onmouseout".
*/
-void DilloHtmlReceiver::leave (dw::core::ui::Resource *resource)
+void DilloHtmlReceiver::leave (Resource *resource)
{
DilloHtml *html = form->html;
a_UIcmd_set_msg(html->bw, "");
}
-void DilloHtmlReceiver::clicked (dw::core::ui::ButtonResource *resource,
- int buttonNo, int x, int y)
+void DilloHtmlReceiver::clicked (Resource *resource,
+ EventButton *event)
{
-// form->eventHandler(resource, x, y);
+ form->eventHandler(resource, event);
}
/*
- * DilloHtmlInput
+ * DilloHtmlInput
*/
/*
- * Constructor
+ * Constructor
*/
-DilloHtmlInput::DilloHtmlInput (DilloHtmlInputType type2,
- dw::core::ui::Embed *embed2,
- const char *name2,
- const char *init_str2,
+DilloHtmlInput::DilloHtmlInput (DilloHtmlInputType type2, Embed *embed2,
+ const char *name2, const char *init_str2,
bool init_val2)
{
type = type2;
@@ -1576,7 +1561,7 @@ DilloHtmlInput::DilloHtmlInput (DilloHtmlInputType type2,
}
/*
- * Destructor
+ * Destructor
*/
DilloHtmlInput::~DilloHtmlInput ()
{
@@ -1592,61 +1577,57 @@ DilloHtmlInput::~DilloHtmlInput ()
*/
void DilloHtmlInput::connectTo(DilloHtmlReceiver *form_receiver)
{
- dw::core::ui::Resource *resource = NULL;
- if (embed)
- resource = embed->getResource ();
- switch (type) {
- case DILLO_HTML_INPUT_UNKNOWN:
- case DILLO_HTML_INPUT_HIDDEN:
- case DILLO_HTML_INPUT_CHECKBOX:
- case DILLO_HTML_INPUT_RADIO:
- case DILLO_HTML_INPUT_BUTTON:
- case DILLO_HTML_INPUT_TEXTAREA:
- case DILLO_HTML_INPUT_SELECT:
- case DILLO_HTML_INPUT_SEL_LIST:
- // do nothing
- break;
- case DILLO_HTML_INPUT_TEXT:
- case DILLO_HTML_INPUT_PASSWORD:
- case DILLO_HTML_INPUT_INDEX:
- case DILLO_HTML_INPUT_SUBMIT:
- case DILLO_HTML_INPUT_RESET:
- case DILLO_HTML_INPUT_BUTTON_SUBMIT:
- case DILLO_HTML_INPUT_BUTTON_RESET:
- case DILLO_HTML_INPUT_IMAGE:
- case DILLO_HTML_INPUT_FILE:
- if (resource)
+ Resource *resource;
+ if (embed && (resource = embed->getResource())) {
+ resource->connectClicked (form_receiver);
+ if (type == DILLO_HTML_INPUT_SUBMIT ||
+ type == DILLO_HTML_INPUT_RESET ||
+ type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
+ type == DILLO_HTML_INPUT_BUTTON_RESET ||
+ type == DILLO_HTML_INPUT_IMAGE ||
+ type == DILLO_HTML_INPUT_FILE ||
+ type == DILLO_HTML_INPUT_TEXT ||
+ type == DILLO_HTML_INPUT_PASSWORD ||
+ type == DILLO_HTML_INPUT_INDEX) {
resource->connectActivate (form_receiver);
- break;
+ }
}
}
/*
- * Activate a form
+ * Activate a form
*/
-void DilloHtmlInput::activate(DilloHtmlForm *form, bool force_submit)
+void DilloHtmlInput::activate(DilloHtmlForm *form, int num_entry_fields,
+ EventButton *event)
{
switch (type) {
- case DILLO_HTML_INPUT_TEXT:
- case DILLO_HTML_INPUT_PASSWORD:
- if (force_submit)
- form->submit (this);
- break;
case DILLO_HTML_INPUT_FILE:
readFile (form->html->bw);
break;
case DILLO_HTML_INPUT_RESET:
case DILLO_HTML_INPUT_BUTTON_RESET:
- form->reset ();
+ form->reset();
+ break;
+ case DILLO_HTML_INPUT_TEXT:
+ case DILLO_HTML_INPUT_PASSWORD:
+ if (!(prefs.enterpress_forces_submit || num_entry_fields == 1)) {
+ break;
+ } else {
+ /* fall through */
+ }
+ case DILLO_HTML_INPUT_SUBMIT:
+ case DILLO_HTML_INPUT_BUTTON_SUBMIT:
+ case DILLO_HTML_INPUT_IMAGE:
+ case DILLO_HTML_INPUT_INDEX:
+ form->submit(this, event);
break;
default:
- form->submit (this);
break;
}
}
/*
- * Read a file into cache
+ * Read a file into cache
*/
void DilloHtmlInput::readFile (BrowserWindow *bw)
{
@@ -1657,8 +1638,7 @@ void DilloHtmlInput::readFile (BrowserWindow *bw)
file_data = a_Misc_file2dstr(filename);
if (file_data) {
a_UIcmd_set_msg(bw, "File loaded.");
- dw::core::ui::LabelButtonResource *lbr =
- (dw::core::ui::LabelButtonResource*)embed->getResource();
+ LabelButtonResource *lbr = (LabelButtonResource*)embed->getResource();
lbr->setLabel(filename);
} else {
a_UIcmd_set_msg(bw, "ERROR: can't load: %s", filename);
@@ -1675,24 +1655,24 @@ void DilloHtmlInput::appendValuesTo(Dlist *values, bool is_active_submit)
case DILLO_HTML_INPUT_TEXT:
case DILLO_HTML_INPUT_PASSWORD:
case DILLO_HTML_INPUT_INDEX:
+ case DILLO_HTML_INPUT_HIDDEN:
{
- dw::core::ui::EntryResource *entryres =
- (dw::core::ui::EntryResource*)embed->getResource();
+ EntryResource *entryres = (EntryResource*)embed->getResource();
dList_append(values, dStr_new(entryres->getText()));
}
break;
case DILLO_HTML_INPUT_TEXTAREA:
{
- dw::core::ui::MultiLineTextResource *textres =
- (dw::core::ui::MultiLineTextResource*)embed->getResource();
+ MultiLineTextResource *textres =
+ (MultiLineTextResource*)embed->getResource();
dList_append(values, dStr_new(textres->getText()));
}
break;
case DILLO_HTML_INPUT_CHECKBOX:
case DILLO_HTML_INPUT_RADIO:
{
- dw::core::ui::ToggleButtonResource *cb_r =
- (dw::core::ui::ToggleButtonResource*)embed->getResource();
+ ToggleButtonResource *cb_r =
+ (ToggleButtonResource*)embed->getResource();
if (name && init_str && cb_r->isActivated()) {
dList_append(values, dStr_new(init_str));
}
@@ -1703,21 +1683,16 @@ void DilloHtmlInput::appendValuesTo(Dlist *values, bool is_active_submit)
if (is_active_submit)
dList_append(values, dStr_new(init_str));
break;
- case DILLO_HTML_INPUT_HIDDEN:
- dList_append(values, dStr_new(init_str));
- break;
case DILLO_HTML_INPUT_SELECT:
case DILLO_HTML_INPUT_SEL_LIST:
- { // brackets for compiler happiness.
- dw::core::ui::SelectionResource *sel_res =
- (dw::core::ui::SelectionResource*)embed->getResource();
+ {
+ SelectionResource *sel_res = (SelectionResource*)embed->getResource();
select->appendValuesTo (values, sel_res);
}
break;
case DILLO_HTML_INPUT_FILE:
{
- dw::core::ui::LabelButtonResource *lbr =
- (dw::core::ui::LabelButtonResource*)embed->getResource();
+ LabelButtonResource *lbr = (LabelButtonResource*)embed->getResource();
const char *filename = lbr->getLabel();
if (filename[0] && strcmp(filename, init_str)) {
if (file_data) {
@@ -1732,8 +1707,8 @@ void DilloHtmlInput::appendValuesTo(Dlist *values, bool is_active_submit)
break;
case DILLO_HTML_INPUT_IMAGE:
if (is_active_submit) {
- dw::core::ui::ComplexButtonResource *cbr =
- (dw::core::ui::ComplexButtonResource*)embed->getResource();
+ ComplexButtonResource *cbr =
+ (ComplexButtonResource*)embed->getResource();
Dstr *strX = dStr_new("");
Dstr *strY = dStr_new("");
dStr_sprintf(strX, "%d", cbr->getClickX());
@@ -1755,17 +1730,18 @@ void DilloHtmlInput::reset ()
switch (type) {
case DILLO_HTML_INPUT_TEXT:
case DILLO_HTML_INPUT_PASSWORD:
+ case DILLO_HTML_INPUT_INDEX:
+ case DILLO_HTML_INPUT_HIDDEN:
{
- dw::core::ui::EntryResource *entryres =
- (dw::core::ui::EntryResource*)embed->getResource();
+ EntryResource *entryres = (EntryResource*)embed->getResource();
entryres->setText(init_str ? init_str : "");
}
break;
case DILLO_HTML_INPUT_CHECKBOX:
case DILLO_HTML_INPUT_RADIO:
{
- dw::core::ui::ToggleButtonResource *tb_r =
- (dw::core::ui::ToggleButtonResource*)embed->getResource();
+ ToggleButtonResource *tb_r =
+ (ToggleButtonResource*)embed->getResource();
tb_r->setActivated(init_val);
}
break;
@@ -1801,15 +1777,14 @@ void DilloHtmlInput::reset ()
break;
case DILLO_HTML_INPUT_TEXTAREA:
if (init_str != NULL) {
- dw::core::ui::MultiLineTextResource *textres =
- (dw::core::ui::MultiLineTextResource*)embed->getResource();
+ MultiLineTextResource *textres =
+ (MultiLineTextResource*)embed->getResource();
textres->setText(init_str ? init_str : "");
}
break;
case DILLO_HTML_INPUT_FILE:
{
- dw::core::ui::LabelButtonResource *lbr =
- (dw::core::ui::LabelButtonResource*)embed->getResource();
+ LabelButtonResource *lbr = (LabelButtonResource*)embed->getResource();
lbr->setLabel(init_str);
}
break;
@@ -1819,11 +1794,11 @@ void DilloHtmlInput::reset ()
}
/*
- * DilloHtmlSelect
+ * DilloHtmlSelect
*/
/*
- * Constructor
+ * Constructor
*/
DilloHtmlSelect::DilloHtmlSelect ()
{
@@ -1831,7 +1806,7 @@ DilloHtmlSelect::DilloHtmlSelect ()
}
/*
- * Destructor
+ * Destructor
*/
DilloHtmlSelect::~DilloHtmlSelect ()
{
@@ -1872,7 +1847,7 @@ void DilloHtmlSelect::ensureSelection()
}
}
-void DilloHtmlSelect::addOptionsTo (dw::core::ui::SelectionResource *res)
+void DilloHtmlSelect::addOptionsTo (SelectionResource *res)
{
int size = options->size ();
for (int i = 0; i < size; i++) {
@@ -1881,8 +1856,7 @@ void DilloHtmlSelect::addOptionsTo (dw::core::ui::SelectionResource *res)
}
}
-void DilloHtmlSelect::appendValuesTo (Dlist *values,
- dw::core::ui::SelectionResource *res)
+void DilloHtmlSelect::appendValuesTo (Dlist *values, SelectionResource *res)
{
int size = options->size ();
for (int i = 0; i < size; i++) {
@@ -1895,11 +1869,11 @@ void DilloHtmlSelect::appendValuesTo (Dlist *values,
}
/*
- * DilloHtmlOption
+ * DilloHtmlOption
*/
/*
- * Constructor
+ * Constructor
*/
DilloHtmlOption::DilloHtmlOption (char *value2,
bool selected2,
@@ -1912,7 +1886,7 @@ DilloHtmlOption::DilloHtmlOption (char *value2,
}
/*
- * Destructor
+ * Destructor
*/
DilloHtmlOption::~DilloHtmlOption ()
{
@@ -1921,48 +1895,38 @@ DilloHtmlOption::~DilloHtmlOption ()
}
/*
- * Utilities
+ * Utilities
*/
/*
* Create input image for the form
*/
-static dw::core::ui::Embed *Html_input_image(DilloHtml *html,
- const char *tag, int tagsize)
+static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize)
{
const char *attrbuf;
- StyleAttrs style_attrs;
DilloImage *Image;
- dw::core::ui::Embed *button = NULL;
+ Embed *button = NULL;
DilloUrl *url = NULL;
-
+
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "src")) &&
(url = a_Html_url_new(html, attrbuf, NULL, 0))) {
- style_attrs = *S_TOP(html)->style;
- style_attrs.cursor = CURSOR_POINTER;
- style_attrs.backgroundColor =
- style::Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color);
+
+ html->styleEngine->setPseudoLink ();
/* create new image and add it to the button */
- if ((Image = a_Html_add_new_image(html, tag, tagsize, url, &style_attrs,
- false))) {
- Style *style = Style::create (HT2LT(html), &style_attrs);
- IM2DW(Image)->setStyle (style);
- dw::core::ui::ComplexButtonResource *complex_b_r =
- HT2LT(html)->getResourceFactory()->createComplexButtonResource(
- IM2DW(Image), false);
- button = new dw::core::ui::Embed(complex_b_r);
- DW2TB(html->dw)->addWidget (button, style);
+ if ((Image = a_Html_image_new(html, tag, tagsize, url))) {
+ IM2DW(Image)->setStyle (html->styleEngine->backgroundStyle ());
+ ResourceFactory *factory = HT2LT(html)->getResourceFactory();
+ ComplexButtonResource *complex_b_r =
+ factory->createComplexButtonResource(IM2DW(Image), false);
+ button = new Embed(complex_b_r);
+ HT2TB(html)->addWidget (button, html->styleEngine->style ());
// gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
- style->unref();
- /* a right button press brings up the image menu */
- html->connectSignals((Widget*)Image->dw);
} else {
a_Url_free(url);
}
}
-
if (!button)
MSG("Html_input_image: unable to create image submit.\n");
return button;
@@ -1978,7 +1942,6 @@ static void Html_option_finish(DilloHtml *html)
input->type == DILLO_HTML_INPUT_SEL_LIST) {
DilloHtmlOption *option =
input->select->getCurrentOption ();
- option->content =
- a_Html_parse_entities(html, html->Stash->str, html->Stash->len);
+ option->content = dStrndup(html->Stash->str, html->Stash->len);
}
}
diff --git a/src/form.hh b/src/form.hh
index 8375ba50..a54cde56 100644
--- a/src/form.hh
+++ b/src/form.hh
@@ -4,7 +4,7 @@
#include "url.h"
/*
- * Typedefs
+ * Typedefs
*/
typedef enum {
@@ -14,12 +14,12 @@ typedef enum {
} DilloHtmlMethod;
typedef enum {
- DILLO_HTML_ENC_URLENCODING,
+ DILLO_HTML_ENC_URLENCODED,
DILLO_HTML_ENC_MULTIPART
} DilloHtmlEnc;
/*
- * Classes
+ * Classes
*/
class DilloHtmlForm;
@@ -27,20 +27,24 @@ class DilloHtmlInput;
class DilloHtml;
/*
- * Form API
+ * Form API
*/
DilloHtmlForm *a_Html_form_new(DilloHtml *html,
DilloHtmlMethod method,
const DilloUrl *action,
DilloHtmlEnc enc,
- const char *charset);
+ const char *charset, bool enabled);
void a_Html_form_delete(DilloHtmlForm* form);
void a_Html_input_delete(DilloHtmlInput* input);
+void a_Html_form_submit2(void *v_form);
+void a_Html_form_reset2(void *v_form);
+void a_Html_form_display_hiddens2(void *v_form, bool display);
+
/*
- * Form parsing functions
+ * Form parsing functions
*/
void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize);
diff --git a/src/gif.c b/src/gif.c
index 6ea23561..3df83d20 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -67,10 +67,8 @@
#include "msg.h"
#include "image.hh"
-#include "web.hh"
#include "cache.h"
#include "dicache.h"
-#include "prefs.h"
#define INTERLACE 0x40
#define LOCALCOLORMAP 0x80
@@ -147,54 +145,15 @@ static void Gif_write(DilloGif *gif, void *Buf, uint_t BufSize);
static void Gif_close(DilloGif *gif, CacheClient_t *Client);
static size_t Gif_process_bytes(DilloGif *gif, const uchar_t *buf,
int bufsize, void *Buf);
-static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, int version);
-static void Gif_callback(int Op, CacheClient_t *Client);
-
-/* exported function */
-void *a_Gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
- void **Data);
/*
- * MIME handler for "image/gif" type
- * (Sets Gif_callback as cache-client)
- */
-void *a_Gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
- void **Data)
-{
- DilloWeb *web = Ptr;
- DICacheEntry *DicEntry;
-
- if (!web->Image)
- web->Image = a_Image_new(0, 0, NULL, prefs.bg_color);
- /* TODO: get the backgound color from the parent widget -- Livio. */
-
- /* Add an extra reference to the Image (for dicache usage) */
- a_Image_ref(web->Image);
-
- DicEntry = a_Dicache_get_entry(web->url);
- if (!DicEntry) {
- /* Let's create an entry for this image... */
- DicEntry = a_Dicache_add_entry(web->url);
-
- /* ... and let the decoder feed it! */
- *Data = Gif_new(web->Image, DicEntry->url, DicEntry->version);
- *Call = (CA_Callback_t) Gif_callback;
- } else {
- /* Let's feed our client from the dicache */
- a_Dicache_ref(DicEntry->url, DicEntry->version);
- *Data = web->Image;
- *Call = (CA_Callback_t) a_Dicache_callback;
- }
- return (web->Image->dw);
-}
-
-/*
* Create a new gif structure for decoding a gif into a RGB buffer
*/
-static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, int version)
+void *a_Gif_new(DilloImage *Image, DilloUrl *url, int version)
{
DilloGif *gif = dMalloc(sizeof(DilloGif));
+ _MSG("a_Gif_new: gif=%p\n", gif);
gif->Image = Image;
gif->url = url;
@@ -216,15 +175,38 @@ static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, int version)
}
/*
+ * Free the gif-decoding data structure.
+ */
+static void Gif_free(DilloGif *gif)
+{
+ int i;
+
+ _MSG("Gif_free: gif=%p\n", gif);
+
+ dFree(gif->linebuf);
+ if (gif->spill_lines != NULL) {
+ for (i = 0; i < gif->num_spill_lines_max; i++)
+ dFree(gif->spill_lines[i]);
+ dFree(gif->spill_lines);
+ }
+ dFree(gif);
+}
+
+/*
* This function is a cache client, it receives data from the cache
* and dispatches it to the appropriate gif-processing functions
*/
-static void Gif_callback(int Op, CacheClient_t *Client)
+void a_Gif_callback(int Op, void *data)
{
- if (Op)
- Gif_close(Client->CbData, Client);
- else
+ if (Op == CA_Send) {
+ CacheClient_t *Client = data;
Gif_write(Client->CbData, Client->Buf, Client->BufSize);
+ } else if (Op == CA_Close) {
+ CacheClient_t *Client = data;
+ Gif_close(Client->CbData, Client);
+ } else if (Op == CA_Abort) {
+ Gif_free(data);
+ }
}
/*
@@ -259,20 +241,9 @@ static void Gif_write(DilloGif *gif, void *Buf, uint_t BufSize)
*/
static void Gif_close(DilloGif *gif, CacheClient_t *Client)
{
- int i;
-
- _MSG("destroy gif %p\n", gif);
-
+ _MSG("Gif_close: destroy gif %p\n", gif);
a_Dicache_close(gif->url, gif->version, Client);
-
- dFree(gif->linebuf);
-
- if (gif->spill_lines != NULL) {
- for (i = 0; i < gif->num_spill_lines_max; i++)
- dFree(gif->spill_lines[i]);
- dFree(gif->spill_lines);
- }
- dFree(gif);
+ Gif_free(gif);
}
@@ -418,7 +389,7 @@ static void Gif_lwz_init(DilloGif *gif)
*/
static void Gif_emit_line(DilloGif *gif, const uchar_t *linebuf)
{
- a_Dicache_write(gif->Image, gif->url, gif->version, linebuf, gif->y);
+ a_Dicache_write(gif->url, gif->version, linebuf, gif->y);
if (gif->Flags & INTERLACE) {
switch (gif->pass) {
case 0:
@@ -841,6 +812,16 @@ static size_t Gif_do_img_desc(DilloGif *gif, void *Buf,
gif->Width = LM_to_uint(buf[4], buf[5]);
gif->Height = LM_to_uint(buf[6], buf[7]);
+
+ /* check max image size */
+ if (gif->Width <= 0 || gif->Height <= 0 ||
+ gif->Width > IMAGE_MAX_AREA / gif->Height) {
+ MSG("Gif_do_img_desc: suspicious image size request %ux%u\n",
+ gif->Width, gif->Height);
+ gif->state = 999;
+ return 0;
+ }
+
gif->linebuf = dMalloc(gif->Width);
a_Dicache_set_parms(gif->url, gif->version, gif->Image,
@@ -1045,4 +1026,9 @@ static size_t Gif_process_bytes(DilloGif *gif, const uchar_t *ibuf,
return bufsize - tmp_bufsize;
}
+#else /* ENABLE_GIF */
+
+void *a_Gif_new() { return 0; }
+void a_Gif_callback() { return; }
+
#endif /* ENABLE_GIF */
diff --git a/src/history.c b/src/history.c
index 4b8fb426..15ab19db 100644
--- a/src/history.c
+++ b/src/history.c
@@ -75,22 +75,9 @@ int a_History_add_url(DilloUrl *url)
}
/*
- * Set the page-title for a given URL (by idx)
- * (this is known when the first chunks of HTML data arrive)
- */
-int a_History_set_title(int idx, const char *title)
-{
- dReturn_val_if_fail(idx >= 0 && idx < history_size, 0);
-
- dFree(history[idx].title);
- history[idx].title = dStrdup(title);
- return 1;
-}
-
-/*
* Return the DilloUrl field (by index)
*/
-DilloUrl *a_History_get_url(int idx)
+const DilloUrl *a_History_get_url(int idx)
{
_MSG("a_History_get_url: ");
/* History_show(); */
@@ -137,11 +124,32 @@ const char *a_History_get_title_by_url(const DilloUrl *url, int force)
return NULL;
}
+/*
+ * Set the page-title for a given URL
+ */
+void a_History_set_title_by_url(const DilloUrl *url, const char *title)
+{
+ int i;
+
+ dReturn_if (url == NULL);
+
+ for (i = history_size - 1; i >= 0; --i)
+ if (a_Url_cmp(url, history[i].url) == 0)
+ break;
+
+ if (i >= 0) {
+ dFree(history[i].title);
+ history[i].title = dStrdup(title);
+ } else {
+ MSG_ERR("a_History_set_title_by_url: %s not found\n", URL_STR(url));
+ }
+}
+
/*
* Free all the memory used by this module
*/
-void a_History_free()
+void a_History_freeall()
{
int i;
diff --git a/src/history.h b/src/history.h
index da520c87..beb0cdf9 100644
--- a/src/history.h
+++ b/src/history.h
@@ -10,11 +10,11 @@ extern "C" {
#endif /* __cplusplus */
int a_History_add_url(DilloUrl *url);
-int a_History_set_title(int idx, const char *title);
-DilloUrl *a_History_get_url(int idx);
+void a_History_set_title_by_url(const DilloUrl *url, const char *title);
+const DilloUrl *a_History_get_url(int idx);
const char *a_History_get_title(int idx, int force);
const char *a_History_get_title_by_url(const DilloUrl *url, int force);
-void a_History_free(void);
+void a_History_freeall(void);
#ifdef __cplusplus
diff --git a/src/html.cc b/src/html.cc
index 70466979..49d06da3 100644
--- a/src/html.cc
+++ b/src/html.cc
@@ -20,20 +20,17 @@
#include <string.h> /* for memcpy and memmove */
#include <stdlib.h>
#include <stdio.h> /* for sprintf */
-#include <math.h> /* for rint */
#include <errno.h>
-#include <fltk/utf.h> /* for utf8encode */
-
#include "bw.h" /* for BrowserWindow */
#include "msg.h"
#include "binaryconst.h"
#include "colors.h"
+#include "utf8.hh"
#include "misc.h"
#include "uicmd.hh"
#include "history.h"
-#include "nav.h"
#include "menu.hh"
#include "prefs.h"
#include "capi.h"
@@ -49,7 +46,7 @@
#include "dw/ruler.hh"
/*-----------------------------------------------------------------------------
- * Defines
+ * Defines
*---------------------------------------------------------------------------*/
/* Define to 1 to ignore white space immediately after an open tag,
@@ -61,6 +58,7 @@
/*-----------------------------------------------------------------------------
* Name spaces
*---------------------------------------------------------------------------*/
+using namespace lout;
using namespace dw;
using namespace dw::core;
using namespace dw::core::ui;
@@ -106,24 +104,15 @@ static const char *Html_get_attr2(DilloHtml *html,
int tagsize,
const char *attrname,
int tag_parsing_flags);
-static void Html_add_widget(DilloHtml *html, Widget *widget,
- char *width_str, char *height_str,
- StyleAttrs *style_attrs);
static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof);
-static void Html_load_image(BrowserWindow *bw, DilloUrl *url,
- DilloImage *image);
+static bool Html_load_image(BrowserWindow *bw, DilloUrl *url,
+ const DilloUrl *requester, DilloImage *image);
static void Html_callback(int Op, CacheClient_t *Client);
-static int Html_tag_index(const char *tag);
static void Html_tag_cleanup_at_close(DilloHtml *html, int TagIdx);
/*-----------------------------------------------------------------------------
* Local Data
*---------------------------------------------------------------------------*/
-/* The following array of font sizes has to be _strictly_ increasing */
-static const int FontSizes[] = {10, 12, 14, 18, 22, 28};
-static const int FontSizesNum = 6;
-static const int FontSizesBase = 2;
-
/* Parsing table structure */
typedef struct {
const char *name; /* element name */
@@ -210,7 +199,7 @@ static void Html_free(void *data)
/*
* Used by the "Load images" page menuitem.
- */
+ */
void a_Html_load_images(void *v_html, DilloUrl *pattern)
{
DilloHtml *html = (DilloHtml*)v_html;
@@ -219,6 +208,58 @@ void a_Html_load_images(void *v_html, DilloUrl *pattern)
}
/*
+ * Search for form
+ */
+static bool Html_contains_form(DilloHtml *html, void *v_form)
+{
+ for (int i = 0; i < html->forms->size(); i++) {
+ if (html->forms->get(i) == v_form) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Used by the "Submit form" form menuitem.
+ */
+void a_Html_form_submit(void *v_html, void *v_form)
+{
+ DilloHtml *html = (DilloHtml*)v_html;
+
+ if (Html_contains_form(html, v_form)) {
+ /* it's still valid */
+ a_Html_form_submit2(v_form);
+ }
+}
+
+/*
+ * Used by the "Reset form" form menuitem.
+ */
+void a_Html_form_reset(void *v_html, void *v_form)
+{
+ DilloHtml *html = (DilloHtml*)v_html;
+
+ if (Html_contains_form(html, v_form)) {
+ /* it's still valid */
+ a_Html_form_reset2(v_form);
+ }
+}
+
+/*
+ * Used by the "Show/Hide hiddens" form menuitem.
+ */
+void a_Html_form_display_hiddens(void *v_html, void *v_form, bool_t display)
+{
+ DilloHtml *html = (DilloHtml*)v_html;
+
+ if (Html_contains_form(html, v_form)) {
+ /* it's still valid */
+ a_Html_form_display_hiddens2(v_form, (display != 0));
+ }
+}
+
+/*
* Set the URL data for image maps.
*/
static void Html_set_link_coordinates(DilloHtml *html, int link, int x, int y)
@@ -244,42 +285,20 @@ static int Html_set_new_link(DilloHtml *html, DilloUrl **url)
}
/*
- * Add a new image.
+ * Add a new image to our list.
+ * image is NULL if dillo will try to load the image immediately.
*/
-static int Html_add_new_linkimage(DilloHtml *html,
- DilloUrl **url, DilloImage *image)
+static void Html_add_new_htmlimage(DilloHtml *html,
+ DilloUrl **url, DilloImage *image)
{
- DilloLinkImage *li = dNew(DilloLinkImage, 1);
- li->url = *url;
- li->image = image;
+ DilloHtmlImage *hi = dNew(DilloHtmlImage, 1);
+ hi->url = *url;
+ hi->image = image;
+ a_Image_ref(image);
- int ni = html->images->size();
+ int n = html->images->size();
html->images->increase();
- html->images->set(ni, li);
- return ni;
-}
-
-/*
- * Set the font at the top of the stack. BImask specifies which
- * attributes in BI should be changed.
- */
-void a_Html_set_top_font(DilloHtml *html, const char *name, int size,
- int BI, int BImask)
-{
- FontAttrs font_attrs;
-
- font_attrs = *S_TOP(html)->style->font;
- if (name)
- font_attrs.name = name;
- if (size)
- font_attrs.size = size;
- if (BImask & 1)
- font_attrs.weight = (BI & 1) ? 700 : 400;
- if (BImask & 2)
- font_attrs.style = (BI & 2) ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL;
-
- HTML_SET_TOP_ATTR (html, font,
- Font::create (HT2LT(html), &font_attrs));
+ html->images->set(n, hi);
}
/*
@@ -287,25 +306,26 @@ void a_Html_set_top_font(DilloHtml *html, const char *name, int size,
* sets the style at the top of the stack.
*/
void a_Html_tag_set_align_attr(DilloHtml *html,
+ CssPropertyList *props,
const char *tag, int tagsize)
{
- const char *align, *charattr;
+ const char *align;
if ((align = a_Html_get_attr(html, tag, tagsize, "align"))) {
- Style *old_style = S_TOP(html)->style;
- StyleAttrs style_attrs = *old_style;
+ TextAlignType textAlignType = TEXT_ALIGN_LEFT;
if (dStrcasecmp (align, "left") == 0)
- style_attrs.textAlign = TEXT_ALIGN_LEFT;
+ textAlignType = TEXT_ALIGN_LEFT;
else if (dStrcasecmp (align, "right") == 0)
- style_attrs.textAlign = TEXT_ALIGN_RIGHT;
+ textAlignType = TEXT_ALIGN_RIGHT;
else if (dStrcasecmp (align, "center") == 0)
- style_attrs.textAlign = TEXT_ALIGN_CENTER;
+ textAlignType = TEXT_ALIGN_CENTER;
else if (dStrcasecmp (align, "justify") == 0)
- style_attrs.textAlign = TEXT_ALIGN_JUSTIFY;
+ textAlignType = TEXT_ALIGN_JUSTIFY;
+#if 0
else if (dStrcasecmp (align, "char") == 0) {
/* TODO: Actually not supported for <p> etc. */
- style_attrs.textAlign = TEXT_ALIGN_STRING;
+ v.textAlign = TEXT_ALIGN_STRING;
if ((charattr = a_Html_get_attr(html, tag, tagsize, "char"))) {
if (charattr[0] == 0)
/* TODO: ALIGN=" ", and even ALIGN="&32;" will reult in
@@ -319,8 +339,8 @@ void a_Html_tag_set_align_attr(DilloHtml *html,
/* TODO: Examine LANG attr of <html>. */
style_attrs.textAlignChar = '.';
}
- S_TOP(html)->style = Style::create (HT2LT(html), &style_attrs);
- old_style->unref ();
+#endif
+ props->set (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, textAlignType);
}
}
@@ -329,19 +349,22 @@ void a_Html_tag_set_align_attr(DilloHtml *html,
* sets the style in style_attrs. Returns true when set.
*/
bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag,
- int tagsize, StyleAttrs *style_attrs)
+ int tagsize, CssPropertyList *props)
{
const char *attr;
+ VAlignType valign;
if ((attr = a_Html_get_attr(html, tag, tagsize, "valign"))) {
if (dStrcasecmp (attr, "top") == 0)
- style_attrs->valign = VALIGN_TOP;
+ valign = VALIGN_TOP;
else if (dStrcasecmp (attr, "bottom") == 0)
- style_attrs->valign = VALIGN_BOTTOM;
+ valign = VALIGN_BOTTOM;
else if (dStrcasecmp (attr, "baseline") == 0)
- style_attrs->valign = VALIGN_BASELINE;
+ valign = VALIGN_BASELINE;
else
- style_attrs->valign = VALIGN_MIDDLE;
+ valign = VALIGN_MIDDLE;
+
+ props->set (CSS_PROPERTY_VERTICAL_ALIGN, CSS_TYPE_ENUM, valign);
return true;
} else
return false;
@@ -349,84 +372,17 @@ bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag,
/*
- * Add a new DwPage into the current DwPage, for indentation.
- * left and right are the horizontal indentation amounts, space is the
- * vertical space around the block.
+ * Create and add a new Textblock to the current Textblock
*/
-static void Html_add_indented_widget(DilloHtml *html, Widget *textblock,
- int left, int right, int space)
+static void Html_add_textblock(DilloHtml *html, int space)
{
- StyleAttrs style_attrs;
- Style *style;
-
- style_attrs = *S_TOP(html)->style;
-
- style_attrs.margin.setVal (0);
- style_attrs.borderWidth.setVal (0);
- style_attrs.padding.setVal(0);
-
- /* Activate this for debugging */
-#if 0
- style_attrs.borderWidth.setVal (1);
- style_attrs.setBorderColor (
- Color::createShaded (HT2LT(html), style_attrs.color->getColor());
- style_attrs.setBorderStyle (BORDER_DASHED);
-#endif
-
- style_attrs.margin.left = left;
- style_attrs.margin.right = right;
- style = Style::create (HT2LT(html), &style_attrs);
+ Textblock *textblock = new Textblock (prefs.limit_text_width);
- DW2TB(html->dw)->addParbreak (space, style);
- DW2TB(html->dw)->addWidget (textblock, style);
- DW2TB(html->dw)->addParbreak (space, style);
+ HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ());
+ HT2TB(html)->addWidget (textblock, html->styleEngine->style ());
+ HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ());
S_TOP(html)->textblock = html->dw = textblock;
S_TOP(html)->hand_over_break = true;
- style->unref ();
-
- /* Handle it when the user clicks on a link */
- html->connectSignals(textblock);
-}
-
-/*
- * Create and add a new indented DwPage to the current DwPage
- */
-static void Html_add_indented(DilloHtml *html, int left, int right, int space)
-{
- Textblock *textblock = new Textblock (prefs.limit_text_width);
- Html_add_indented_widget (html, textblock, left, right, space);
-}
-
-/*
- * Given a font_size, this will return the correct 'level'.
- * (or the closest, if the exact level isn't found).
- */
-static int Html_fontsize_to_level(int fontsize)
-{
- int i, level;
- double normalized_size = fontsize / prefs.font_factor,
- approximation = FontSizes[FontSizesNum-1] + 1;
-
- for (i = level = 0; i < FontSizesNum; i++)
- if (approximation >= fabs(normalized_size - FontSizes[i])) {
- approximation = fabs(normalized_size - FontSizes[i]);
- level = i;
- } else {
- break;
- }
-
- return level;
-}
-
-/*
- * Given a level of a font, this will return the correct 'size'.
- */
-static int Html_level_to_fontsize(int level)
-{
- level = MAX(0, level);
- level = MIN(FontSizesNum - 1, level);
-
- return (int)rint(FontSizes[level]*prefs.font_factor);
}
/*
@@ -435,19 +391,19 @@ static int Html_level_to_fontsize(int level)
DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
const char *content_type)
{
- /* Init event receiver */
- linkReceiver.html = this;
-
/* Init main variables */
bw = p_bw;
page_url = a_Url_dup(url);
base_url = a_Url_dup(url);
dw = NULL;
+ /* Init event receiver */
+ linkReceiver.html = this;
+ HT2LT(this)->connectLink (&linkReceiver);
+
a_Bw_add_doc(p_bw, this);
/* Init for-parsing variables */
- Buf_Consumed = 0;
Start_Buf = NULL;
Start_Ofs = 0;
@@ -466,10 +422,13 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
DocType = DT_NONE; /* assume Tag Soup 0.0! :-) */
DocTypeVersion = 0.0f;
+ styleEngine = new StyleEngine (HT2LT (this));
+
+ cssUrls = new misc::SimpleVector <DilloUrl*> (1);
+
stack = new misc::SimpleVector <DilloHtmlState> (16);
stack->increase();
- stack->getRef(0)->style = NULL;
- stack->getRef(0)->table_cell_style = NULL;
+ stack->getRef(0)->table_cell_props = NULL;
stack->getRef(0)->parse_mode = DILLO_HTML_PARSE_MODE_INIT;
stack->getRef(0)->table_mode = DILLO_HTML_TABLE_MODE_NONE;
stack->getRef(0)->cell_text_align_set = false;
@@ -479,7 +438,6 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
stack->getRef(0)->textblock = NULL;
stack->getRef(0)->table = NULL;
stack->getRef(0)->ref_list_item = NULL;
- stack->getRef(0)->current_bg_color = prefs.bg_color;
stack->getRef(0)->hand_over_break = false;
InFlags = IN_NONE;
@@ -490,29 +448,24 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
pre_column = 0;
PreFirstChar = false;
PrevWasCR = false;
- PrevWasOpenTag = false;
- PrevWasSPC = false;
InVisitedLink = false;
ReqTagClose = false;
- CloseOneTag = false;
TagSoup = true;
- NameVal = NULL;
+ loadCssFromStash = false;
Num_HTML = Num_HEAD = Num_BODY = Num_TITLE = 0;
attr_data = dStr_sized_new(1024);
- parse_finished = false;
+ non_css_link_color = -1;
+ non_css_visited_color = -1;
+ visited_color = -1;
/* Init page-handling variables */
forms = new misc::SimpleVector <DilloHtmlForm*> (1);
inputs_outside_form = new misc::SimpleVector <DilloHtmlInput*> (1);
links = new misc::SimpleVector <DilloUrl*> (64);
- images = new misc::SimpleVector <DilloLinkImage*> (16);
- //a_Dw_image_map_list_init(&maps);
-
- link_color = prefs.link_color;
- visited_color = prefs.visited_color;
+ images = new misc::SimpleVector <DilloHtmlImage*> (16);
/* Initialize the main widget */
initDw();
@@ -521,34 +474,15 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
}
/*
- * Miscelaneous initializations for Dw
+ * Miscellaneous initializations for Dw
*/
void DilloHtml::initDw()
{
- StyleAttrs style_attrs;
- FontAttrs font_attrs;
-
dReturn_if_fail (dw == NULL);
/* Create the main widget */
dw = stack->getRef(0)->textblock = new Textblock (prefs.limit_text_width);
- /* Create a dummy font, attribute, and tag for the bottom of the stack. */
- font_attrs.name = prefs.vw_fontname;
- font_attrs.size = Html_level_to_fontsize(FontSizesBase);
- font_attrs.weight = 400;
- font_attrs.style = FONT_STYLE_NORMAL;
-
- style_attrs.initValues ();
- style_attrs.font = Font::create (HT2LT(this), &font_attrs);
- style_attrs.color = Color::createSimple (HT2LT(this), prefs.text_color);
- stack->getRef(0)->style = Style::create (HT2LT(this), &style_attrs);
-
- stack->getRef(0)->table_cell_style = NULL;
-
- /* Handle it when the user clicks on a link */
- connectSignals(dw);
-
bw->num_page_bugs = 0;
dStr_truncate(bw->page_bugs, 0);
}
@@ -560,14 +494,17 @@ DilloHtml::~DilloHtml()
{
_MSG("::~DilloHtml(this=%p)\n", this);
- if (!parse_finished)
- freeParseData();
+ freeParseData();
a_Bw_remove_doc(bw, this);
a_Url_free(page_url);
a_Url_free(base_url);
+ for (int i = 0; i < cssUrls->size(); i++)
+ a_Url_free(cssUrls->get(i));
+ delete (cssUrls);
+
for (int i = 0; i < forms->size(); i++)
a_Html_form_delete (forms->get(i));
delete(forms);
@@ -577,28 +514,18 @@ DilloHtml::~DilloHtml()
delete(inputs_outside_form);
for (int i = 0; i < links->size(); i++)
- if (links->get(i))
- a_Url_free(links->get(i));
+ a_Url_free(links->get(i));
delete (links);
for (int i = 0; i < images->size(); i++) {
- DilloLinkImage *li = images->get(i);
- a_Url_free(li->url);
- if (li->image)
- a_Image_unref(li->image);
- dFree(li);
+ DilloHtmlImage *img = images->get(i);
+ a_Url_free(img->url);
+ a_Image_unref(img->image);
+ dFree(img);
}
delete (images);
- //a_Dw_image_map_list_free(&maps);
-}
-
-/*
- * Connect all signals of a textblock or an image.
- */
-void DilloHtml::connectSignals(Widget *dw)
-{
- dw->connectLink (&linkReceiver);
+ delete styleEngine;
}
/*
@@ -611,9 +538,19 @@ void DilloHtml::write(char *Buf, int BufSize, int Eof)
char *buf = Buf + Start_Ofs;
int bufsize = BufSize - Start_Ofs;
- dReturn_if_fail (dw != NULL);
+ _MSG("DilloHtml::write BufSize=%d Start_Ofs=%d\n", BufSize, Start_Ofs);
+#if 0
+ char *aux = dStrndup(Buf, BufSize);
+ MSG(" {%s}\n", aux);
+ dFree(aux);
+#endif
+ /* Update Start_Buf. It may be used after the parser is stopped */
Start_Buf = Buf;
+
+ dReturn_if (dw == NULL);
+ dReturn_if (stop_parser == true);
+
token_start = Html_write_raw(this, buf, bufsize, Eof);
Start_Ofs += token_start;
}
@@ -644,7 +581,9 @@ int DilloHtml::getCurTagLineNumber()
*/
void DilloHtml::freeParseData()
{
- (stack->getRef(0)->style)->unref (); /* template style */
+ for (int i = stack->size () - 1; i >= 0; i--)
+ if (stack->getRef (i)->table_cell_props)
+ stack->getRef (i)->table_cell_props->unref ();
delete(stack);
dStr_free(Stash, TRUE);
@@ -661,6 +600,8 @@ void DilloHtml::finishParsing(int ClientKey)
{
int si;
+ dReturn_if (stop_parser == true);
+
/* force the close of elements left open (TODO: not for XHTML) */
while ((si = stack->size() - 1)) {
if (stack->getRef(si)->tag_idx != -1) {
@@ -669,9 +610,6 @@ void DilloHtml::finishParsing(int ClientKey)
}
/* Remove this client from our active list */
a_Bw_close_client(bw, ClientKey);
-
- freeParseData();
- parse_finished = true;
}
/*
@@ -680,7 +618,10 @@ void DilloHtml::finishParsing(int ClientKey)
int DilloHtml::formNew(DilloHtmlMethod method, const DilloUrl *action,
DilloHtmlEnc enc, const char *charset)
{
- DilloHtmlForm *form = a_Html_form_new (this,method,action,enc,charset);
+ // avoid data loss on repush after CSS stylesheets have been loaded
+ bool enabled = bw->NumPendingStyleSheets == 0;
+ DilloHtmlForm *form = a_Html_form_new (this, method, action,
+ enc, charset, enabled);
int nf = forms->size ();
forms->increase ();
forms->set (nf, form);
@@ -713,16 +654,36 @@ void DilloHtml::loadImages (const DilloUrl *pattern)
{
dReturn_if_fail (bw->nav_expecting == FALSE);
+ /* If the user asked for a specific URL, the user (NULL) is the requester,
+ * but if the user just asked for all URLs, use the page URL as the
+ * requester. If the possible patterns become more complex, it might be
+ * good to have the caller supply the requester instead.
+ */
+ const DilloUrl *requester = pattern ? NULL : this->page_url;
+
for (int i = 0; i < images->size(); i++) {
if (images->get(i)->image) {
if ((!pattern) || (!a_Url_cmp(images->get(i)->url, pattern))) {
- Html_load_image(bw, images->get(i)->url, images->get(i)->image);
- images->get(i)->image = NULL; // web owns it now
+ if (Html_load_image(bw, images->get(i)->url, requester,
+ images->get(i)->image)) {
+ a_Image_unref (images->get(i)->image);
+ images->get(i)->image = NULL; // web owns it now
+ }
}
}
}
}
+/*
+ * Save URL in a vector (may be loaded later).
+ */
+void DilloHtml::addCssUrl(const DilloUrl *url)
+{
+ int nu = cssUrls->size();
+ cssUrls->increase();
+ cssUrls->set(nu, a_Url_dup(url));
+}
+
bool DilloHtml::HtmlLinkReceiver::enter (Widget *widget, int link, int img,
int x, int y)
{
@@ -732,10 +693,12 @@ bool DilloHtml::HtmlLinkReceiver::enter (Widget *widget, int link, int img,
if (link == -1) {
_MSG(" Link LEAVE notify...\n");
a_UIcmd_set_msg(bw, "");
+ a_UIcmd_set_pointer_on_link(bw, FALSE);
} else {
_MSG(" Link ENTER notify...\n");
Html_set_link_coordinates(html, link, x, y);
a_UIcmd_set_msg(bw, "%s", URL_STR(html->links->get(link)));
+ a_UIcmd_set_pointer_on_link(bw, TRUE);
}
return true;
}
@@ -758,14 +721,12 @@ bool DilloHtml::HtmlLinkReceiver::press (Widget *widget, int link, int img,
if (link != -1)
linkurl = html->links->get(link);
const bool_t loaded_img = (html->images->get(img)->image == NULL);
- a_UIcmd_image_popup(
- bw, html->images->get(img)->url, loaded_img, linkurl);
+ a_UIcmd_image_popup(bw, html->images->get(img)->url, loaded_img,
+ html->page_url, linkurl);
ret = true;
} else {
if (link == -1) {
- a_UIcmd_page_popup(bw, a_History_get_url(NAV_TOP_UIDX(bw)),
- bw->num_page_bugs != 0,
- html->unloadedImages());
+ a_UIcmd_page_popup(bw, bw->num_page_bugs != 0, html->cssUrls);
ret = true;
} else {
a_UIcmd_link_popup(bw, html->links->get(link));
@@ -786,22 +747,12 @@ bool DilloHtml::HtmlLinkReceiver::click (Widget *widget, int link, int img,
if ((img != -1) && (html->images->get(img)->image)) {
// clicked an image that has not already been loaded
- DilloUrl *pattern;
-
if (event->button == 1){
// load all instances of this image
- pattern = html->images->get(img)->url;
- } else {
- if (event->button == 2){
- // load all images
- pattern = NULL;
- } else {
- return false;
- }
+ DilloUrl *pattern = html->images->get(img)->url;
+ html->loadImages(pattern);
+ return true;
}
-
- html->loadImages(pattern);
- return true;
}
if (link != -1) {
@@ -892,7 +843,7 @@ static const Ent_t Entities[NumEnt] = {
{"loz",022712}, {"lrm",020016}, {"lsaquo",020071},{"lsquo",020030},
{"lt",60}, {"macr",0257}, {"mdash",020024},{"micro",0265},
{"middot",0267},{"minus",021022},{"mu",01674}, {"nabla",021007},
- {"nbsp",32}, {"ndash",020023},{"ne",021140}, {"ni",021013},
+ {"nbsp",0240}, {"ndash",020023},{"ne",021140}, {"ni",021013},
{"not",0254}, {"notin",021011},{"nsub",021204}, {"ntilde",0361},
{"nu",01675}, {"oacute",0363}, {"ocirc",0364}, {"oelig",0523},
{"ograve",0362},{"oline",020076},{"omega",01711}, {"omicron",01677},
@@ -952,14 +903,14 @@ static int Html_ms_stupid_quotes_2ucs(int isocode)
{
int ret;
switch (isocode) {
- case 145:
- case 146: ret = '\''; break;
- case 147:
- case 148: ret = '"'; break;
- case 149: ret = 176; break;
- case 150:
- case 151: ret = '-'; break;
- default: ret = isocode; break;
+ case 145:
+ case 146: ret = '\''; break;
+ case 147:
+ case 148: ret = '"'; break;
+ case 149: ret = 176; break;
+ case 150:
+ case 151: ret = '-'; break;
+ default: ret = isocode; break;
}
return ret;
}
@@ -991,7 +942,7 @@ static int Html_parse_entity(DilloHtml *html, const char *token,
/* strtol with base 16 accepts leading "0x" - we don't */
if (*s == '0' && s[1] == 'x') {
s++;
- isocode = 0;
+ isocode = 0;
} else {
isocode = strtol(s, &s, 16);
}
@@ -1015,7 +966,7 @@ static int Html_parse_entity(DilloHtml *html, const char *token,
} else if (isalpha(*s)) {
/* character entity reference */
- while (*++s && (isalnum(*s) || strchr(":_.-", *s)));
+ while (*++s && (isalnum(*s) || strchr(":_.-", *s))) ;
c = *s;
*s = 0;
@@ -1066,7 +1017,7 @@ char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize)
toksize-i, &entsize)) >= 0) {
if (isocode >= 128) {
/* multibyte encoding */
- n = utf8encode(isocode, buf);
+ n = a_Utf8_encode(isocode, buf);
for (k = 0; k < n; ++k)
new_str[j++] = buf[k];
} else {
@@ -1082,9 +1033,32 @@ char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize)
}
/*
+ * For white-space: pre-line, we must break the line if encountering a newline.
+ * Otherwise, collapse whitespace as usual.
+ */
+static void Html_process_space_pre_line(DilloHtml *html, const char *space,
+ int spacesize)
+{
+ int i, breakCnt = 0;
+
+ for (i = 0; i < spacesize; i++) {
+ /* Support for "\r", "\n" and "\r\n" line breaks */
+ if (space[i] == '\r' || (space[i] == '\n' && !html->PrevWasCR)) {
+ breakCnt++;
+ html->PrevWasCR = (space[i] == '\r');
+
+ HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
+ }
+ }
+ if (breakCnt == 0) {
+ HT2TB(html)->addSpace(html->styleEngine->wordStyle ());
+ }
+}
+
+/*
* Parse spaces
*/
-static void Html_process_space(DilloHtml *html, const char *space,
+static void Html_process_space(DilloHtml *html, const char *space,
int spacesize)
{
char *spc;
@@ -1108,11 +1082,12 @@ static void Html_process_space(DilloHtml *html, const char *space,
if (spaceCnt) {
spc = dStrnfill(spaceCnt, ' ');
- DW2TB(html->dw)->addText (spc, S_TOP(html)->style);
+ HT2TB(html)->addText (spc, spaceCnt,
+ html->styleEngine->wordStyle ());
dFree(spc);
spaceCnt = 0;
}
- DW2TB(html->dw)->addLinebreak (S_TOP(html)->style);
+ HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
html->pre_column = 0;
}
html->PreFirstChar = false;
@@ -1140,16 +1115,18 @@ static void Html_process_space(DilloHtml *html, const char *space,
if (spaceCnt) {
spc = dStrnfill(spaceCnt, ' ');
- DW2TB(html->dw)->addText (spc, S_TOP(html)->style);
+ HT2TB(html)->addText (spc, spaceCnt, html->styleEngine->wordStyle ());
dFree(spc);
}
} else {
if (SGML_SPCDEL) {
- /* SGML_SPCDEL ignores white space inmediately after an open tag */
- } else if (!html->PrevWasSPC) {
- DW2TB(html->dw)->addSpace(S_TOP(html)->style);
- html->PrevWasSPC = true;
+ /* SGML_SPCDEL ignores white space immediately after an open tag */
+ } else if (html->styleEngine->wordStyle ()->whiteSpace ==
+ WHITE_SPACE_PRE_LINE) {
+ Html_process_space_pre_line(html, space, spacesize);
+ } else {
+ HT2TB(html)->addSpace(html->styleEngine->wordStyle ());
}
if (parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY)
@@ -1168,7 +1145,7 @@ static void Html_process_space(DilloHtml *html, const char *space,
static void Html_process_word(DilloHtml *html, const char *word, int size)
{
int i, j, start;
- char *Pword, ch;
+ char *Pword;
DilloHtmlParseMode parse_mode = S_TOP(html)->parse_mode;
if (parse_mode == DILLO_HTML_PARSE_MODE_STASH ||
@@ -1195,39 +1172,66 @@ static void Html_process_word(DilloHtml *html, const char *word, int size)
Pword = a_Html_parse_entities(html, word, size);
for (start = i = 0; Pword[i]; start = i)
if (isspace(Pword[i])) {
- while (Pword[++i] && isspace(Pword[i]));
+ while (Pword[++i] && isspace(Pword[i])) ;
Html_process_space(html, Pword + start, i - start);
} else {
- while (Pword[++i] && !isspace(Pword[i]));
- ch = Pword[i];
- Pword[i] = 0;
- DW2TB(html->dw)->addText(Pword, S_TOP(html)->style);
- Pword[i] = ch;
+ while (Pword[++i] && !isspace(Pword[i])) ;
+ HT2TB(html)->addText(Pword + start, i - start,
+ html->styleEngine->wordStyle ());
html->pre_column += i - start;
html->PreFirstChar = false;
}
dFree(Pword);
} else {
+ const char *word2, *word2_end;
+
+ Pword = NULL;
if (!memchr(word,'&', size)) {
/* No entities */
- DW2TB(html->dw)->addText(word, S_TOP(html)->style);
+ word2 = word;
+ word2_end = word + size - 1;
} else {
/* Collapse white-space entities inside the word (except &nbsp;) */
Pword = a_Html_parse_entities(html, word, size);
- for (i = 0; Pword[i]; ++i)
- if (strchr("\t\f\n\r", Pword[i]))
- for (j = i; (Pword[j] = Pword[j+1]); ++j);
-
- DW2TB(html->dw)->addText(Pword, S_TOP(html)->style);
- dFree(Pword);
+ /* Collapse adjacent " \t\f\n\r" characters into a single space */
+ for (i = j = 0; (Pword[i] = Pword[j]); ++i, ++j) {
+ if (strchr(" \t\f\n\r", Pword[i])) {
+ if (i == 0 || (i > 0 && Pword[i-1] != ' '))
+ Pword[i] = ' ';
+ else
+ for (--i; Pword[j+1] && strchr(" \t\f\n\r", Pword[j+1]); ++j)
+ ;
+ }
+ }
+ word2 = Pword;
+ word2_end = word2 + strlen(word2) - 1;
+ }
+ for (start = i = 0; word2[i]; start = i) {
+ int len;
+
+ if (isspace(word2[i])) {
+ while (word2[++i] && isspace(word2[i])) ;
+ Html_process_space(html, word2 + start, i - start);
+ } else if (!strncmp(word2+i, utf8_zero_width_space, 3)) {
+ i += 3;
+ } else if (a_Utf8_ideographic(word2+i, word2_end, &len)) {
+ i += len;
+ HT2TB(html)->addText(word2 + start, i - start,
+ html->styleEngine->wordStyle ());
+ } else {
+ do {
+ i += len;
+ } while (word2[i] && !isspace(word2[i]) &&
+ strncmp(word2+i, utf8_zero_width_space, 3) &&
+ (!a_Utf8_ideographic(word2+i, word2_end, &len)));
+ HT2TB(html)->addText(word2 + start, i - start,
+ html->styleEngine->wordStyle ());
+ }
}
+ if (Pword == word2)
+ dFree(Pword);
}
-
- html->PrevWasOpenTag = false;
- html->PrevWasSPC = false;
- if (html->InFlags & IN_LI)
- html->WordAfterLI = true;
}
/*
@@ -1250,14 +1254,14 @@ static bool Html_match_tag(const char *tagstr, char *tag, int tagsize)
/*
* This function is called after popping the stack, to
- * handle nested DwPage widgets.
+ * handle nested Textblock widgets.
*/
static void Html_eventually_pop_dw(DilloHtml *html, bool hand_over_break)
{
if (html->dw != S_TOP(html)->textblock) {
if (hand_over_break)
- DW2TB(html->dw)->handOverBreak (S_TOP(html)->style);
- DW2TB(html->dw)->flush ();
+ HT2TB(html)->handOverBreak (html->styleEngine->style ());
+ HT2TB(html)->flush ();
html->dw = S_TOP(html)->textblock;
}
}
@@ -1275,10 +1279,8 @@ static void Html_push_tag(DilloHtml *html, int tag_idx)
* instead of copying all fields except for tag. --Jcid */
*html->stack->getRef(n_items) = *html->stack->getRef(n_items - 1);
html->stack->getRef(n_items)->tag_idx = tag_idx;
- /* proper memory management, may be unref'd later */
- (S_TOP(html)->style)->ref ();
- if (S_TOP(html)->table_cell_style)
- (S_TOP(html)->table_cell_style)->ref ();
+ if (S_TOP(html)->table_cell_props)
+ S_TOP(html)->table_cell_props->ref ();
html->dw = S_TOP(html)->textblock;
}
@@ -1288,6 +1290,7 @@ static void Html_push_tag(DilloHtml *html, int tag_idx)
*/
static void Html_force_push_tag(DilloHtml *html, int tag_idx)
{
+ html->styleEngine->startElement (tag_idx);
Html_push_tag(html, tag_idx);
}
@@ -1298,15 +1301,32 @@ static void Html_real_pop_tag(DilloHtml *html)
{
bool hand_over_break;
- (S_TOP(html)->style)->unref ();
- if (S_TOP(html)->table_cell_style)
- (S_TOP(html)->table_cell_style)->unref ();
+ html->styleEngine->endElement (S_TOP(html)->tag_idx);
+ if (S_TOP(html)->table_cell_props)
+ S_TOP(html)->table_cell_props->unref ();
hand_over_break = S_TOP(html)->hand_over_break;
html->stack->setSize (html->stack->size() - 1);
Html_eventually_pop_dw(html, hand_over_break);
}
/*
+ * Cleanup the stack to a given index.
+ */
+static void Html_tag_cleanup_to_idx(DilloHtml *html, int idx)
+{
+ int s_sz;
+ while ((s_sz = html->stack->size()) > idx) {
+ int toptag_idx = S_TOP(html)->tag_idx;
+ TagInfo toptag = Tags[toptag_idx];
+ if (s_sz > idx + 1 && toptag.EndTag != 'O')
+ BUG_MSG(" - forcing close of open tag: <%s>\n", toptag.name);
+ _MSG("Close: %*s%s\n", size," ", toptag.name);
+ toptag.close(html, toptag_idx);
+ Html_real_pop_tag(html);
+ }
+}
+
+/*
* Default close function for tags.
* (conditional cleanup of the stack)
* There are several ways of doing it. Considering the HTML 4.01 spec
@@ -1321,77 +1341,51 @@ static void Html_real_pop_tag(DilloHtml *html)
* 2.- If it exists, clean all the tags in between.
* 3.- Cleanup the matching tag. (on error, give a warning message)
*/
-static void Html_tag_cleanup_at_close(DilloHtml *html, int TagIdx)
+static void Html_tag_cleanup_at_close(DilloHtml *html, int new_idx)
{
int w3c_mode = !prefs.w3c_plus_heuristics;
- int stack_idx, cmp = 1;
- int new_idx = TagIdx;
-
- if (html->CloseOneTag) {
- Html_real_pop_tag(html);
- html->CloseOneTag = false;
- return;
- }
+ int stack_idx, tag_idx, matched = 0, expected = 0;
+ TagInfo new_tag = Tags[new_idx];
/* Look for the candidate tag to close */
- stack_idx = html->stack->size() - 1;
- while (stack_idx &&
- (cmp = (new_idx != html->stack->getRef(stack_idx)->tag_idx)) &&
- ((w3c_mode &&
- Tags[html->stack->getRef(stack_idx)->tag_idx].EndTag == 'O') ||
- (!w3c_mode &&
- (Tags[html->stack->getRef(stack_idx)->tag_idx].EndTag == 'O') ||
- Tags[html->stack->getRef(stack_idx)->tag_idx].TagLevel <
- Tags[new_idx].TagLevel))) {
- --stack_idx;
- }
-
- /* clean, up to the matching tag */
- if (cmp == 0 && stack_idx > 0) {
- /* There's a valid matching tag in the stack */
- while (html->stack->size() > stack_idx) {
- int toptag_idx = S_TOP(html)->tag_idx;
- /* Warn when we decide to close an open tag (for !w3c_mode) */
- if (html->stack->size() > stack_idx + 1 &&
- Tags[toptag_idx].EndTag != 'O')
- BUG_MSG(" - forcing close of open tag: <%s>\n",
- Tags[toptag_idx].name);
-
- /* Close this and only this tag */
- html->CloseOneTag = true;
- Tags[toptag_idx].close (html, toptag_idx);
+ stack_idx = html->stack->size();
+ while (--stack_idx) {
+ tag_idx = html->stack->getRef(stack_idx)->tag_idx;
+ if (tag_idx == new_idx) {
+ /* matching tag found */
+ matched = 1;
+ break;
+ } else if (Tags[tag_idx].EndTag == 'O') {
+ /* skip an optional tag */
+ continue;
+ } else if (w3c_mode || Tags[tag_idx].TagLevel >= new_tag.TagLevel) {
+ /* this is the tag that should have been closed */
+ expected = 1;
+ break;
}
+ }
+ if (matched) {
+ Html_tag_cleanup_to_idx(html, stack_idx);
+ } else if (expected) {
+ BUG_MSG("unexpected closing tag: </%s> -- expected </%s>.\n",
+ new_tag.name, Tags[tag_idx].name);
} else {
- if (stack_idx == 0) {
- BUG_MSG("unexpected closing tag: </%s>.\n", Tags[new_idx].name);
- } else {
- BUG_MSG("unexpected closing tag: </%s>. -- expected </%s>\n",
- Tags[new_idx].name,
- Tags[html->stack->getRef(stack_idx)->tag_idx].name);
- }
+ BUG_MSG("unexpected closing tag: </%s>.\n", new_tag.name);
}
}
/*
- * Cleanup (conditional), and Pop the tag (if it matches)
- */
-void a_Html_pop_tag(DilloHtml *html, int TagIdx)
-{
- Html_tag_cleanup_at_close(html, TagIdx);
-}
-
-/*
* Some parsing routines.
*/
/*
* Used by a_Html_parse_length
*/
-static Length Html_parse_length_or_multi_length (const char *attr,
- char **endptr)
+static CssLength Html_parse_length_or_multi_length (const char *attr,
+ char **endptr)
{
- Length l;
+ CssLength l;
double v;
char *end;
@@ -1399,12 +1393,12 @@ static Length Html_parse_length_or_multi_length (const char *attr,
switch (*end) {
case '%':
end++;
- l = createPerLength (v / 100);
+ l = CSS_CREATE_LENGTH (v / 100, CSS_LENGTH_TYPE_PERCENTAGE);
break;
case '*':
end++;
- l = createRelLength (v);
+ l = CSS_CREATE_LENGTH (v, CSS_LENGTH_TYPE_RELATIVE);
break;
/*
The "px" suffix seems not allowed by HTML4.01 SPEC.
@@ -1413,7 +1407,7 @@ static Length Html_parse_length_or_multi_length (const char *attr,
end += 2;
*/
default:
- l = createAbsLength ((int)v);
+ l = CSS_CREATE_LENGTH (v, CSS_LENGTH_TYPE_PX);
break;
}
@@ -1427,24 +1421,24 @@ static Length Html_parse_length_or_multi_length (const char *attr,
* Returns a length or a percentage, or UNDEF_LENGTH in case
* of an error, or if attr is NULL.
*/
-Length a_Html_parse_length (DilloHtml *html, const char *attr)
+CssLength a_Html_parse_length (DilloHtml *html, const char *attr)
{
- Length l;
+ CssLength l;
char *end;
l = Html_parse_length_or_multi_length (attr, &end);
- if (isRelLength (l))
+ if (CSS_LENGTH_TYPE (l) == CSS_LENGTH_TYPE_RELATIVE)
/* not allowed as &Length; */
- return LENGTH_AUTO;
+ l = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
else {
/* allow only whitespaces */
if (*end && !isspace (*end)) {
BUG_MSG("Garbage after length: %s\n", attr);
- return LENGTH_AUTO;
+ l = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
}
}
- _MSG("a_Html_parse_length: \"%s\" %d\n", attr, absLengthVal(l));
+ _MSG("a_Html_parse_length: \"%s\" %d\n", attr, CSS_LENGTH_VALUE(l));
return l;
}
@@ -1498,7 +1492,7 @@ static int
* recognizes the "HTML Level" with or without the URL. The convention
* comes from mozilla (see URLs below), but Dillo doesn't have the same
* rendering modes, so it may be better to chose another behaviour. --Jcid
- *
+ *
* http://www.mozilla.org/docs/web-developer/quirks/doctypes.html
* http://lists.auriga.wearlab.de/pipermail/dillo-dev/2004-October/002300.html
*
@@ -1507,7 +1501,7 @@ static int
static void Html_parse_doctype(DilloHtml *html, const char *tag, int tagsize)
{
static const char HTML_sig [] = "<!DOCTYPE HTML PUBLIC ";
- static const char HTML20 [] = "-//IETF//DTD HTML//EN";
+ static const char HTML20 [] = "-//IETF//DTD HTML 2.0";
static const char HTML32 [] = "-//W3C//DTD HTML 3.2";
static const char HTML40 [] = "-//W3C//DTD HTML 4.0";
static const char HTML401 [] = "-//W3C//DTD HTML 4.01";
@@ -1524,9 +1518,9 @@ static void Html_parse_doctype(DilloHtml *html, const char *tag, int tagsize)
* and replace '\n' and '\r' with ' ' inside quoted strings. */
for (i = 0, p = ntag; *p; ++p) {
if (isspace(*p)) {
- for (ntag[i++] = ' '; isspace(p[1]); ++p);
+ for (ntag[i++] = ' '; isspace(p[1]); ++p) ;
} else if ((quote = *p) == '"' || *p == '\'') {
- for (ntag[i++] = *p++; (ntag[i++] = *p) && *p != quote; ++p) {
+ for (ntag[i++] = *p++; (ntag[i] = *p) && ntag[i++] != quote; ++p) {
if (*p == '\n' || *p == '\r')
ntag[i - 1] = ' ';
p += (p[0] == '\r' && p[1] == '\n') ? 1 : 0;
@@ -1595,7 +1589,6 @@ static void Html_tag_close_html(DilloHtml *html, int TagIdx)
/* beware of pages with multiple HTML close tags... :-P */
html->InFlags &= ~IN_HTML;
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1622,16 +1615,22 @@ static void Html_tag_open_head(DilloHtml *html, const char *tag, int tagsize)
* Handle close HEAD element
* Note: as a side effect of Html_test_section() this function is called
* twice when the head element is closed implicitly.
+ * Note2: HEAD is parsed once completely got.
*/
static void Html_tag_close_head(DilloHtml *html, int TagIdx)
{
if (html->InFlags & IN_HEAD) {
+ _MSG("Closing HEAD section\n");
if (html->Num_TITLE == 0)
BUG_MSG("HEAD section lacks the TITLE element\n");
-
+
html->InFlags &= ~IN_HEAD;
+
+ /* charset is already set, load remote stylesheets now */
+ for (int i = 0; i < html->cssUrls->size(); i++) {
+ a_Html_load_stylesheet(html, html->cssUrls->get(i));
+ }
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1653,11 +1652,10 @@ static void Html_tag_close_title(DilloHtml *html, int TagIdx)
if (html->InFlags & IN_HEAD) {
/* title is only valid inside HEAD */
a_UIcmd_set_page_title(html->bw, html->Stash->str);
- a_History_set_title(NAV_TOP_UIDX(html->bw),html->Stash->str);
+ a_History_set_title_by_url(html->page_url, html->Stash->str);
} else {
BUG_MSG("the TITLE element must be inside the HEAD section\n");
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1677,16 +1675,33 @@ static void Html_tag_open_script(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_script(DilloHtml *html, int TagIdx)
{
/* eventually the stash will be sent to an interpreter for parsing */
- a_Html_pop_tag(html, TagIdx);
}
/*
* Handle open STYLE
- * store the contents to the stash where (in the future) the style
- * sheet interpreter can get it.
+ * Store contents in the stash where the style sheet interpreter can get it.
*/
static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize)
{
+ const char *attrbuf;
+
+ html->loadCssFromStash = true;
+
+ if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) {
+ BUG_MSG("type attribute is required for <style>\n");
+ } else if (dStrcasecmp(attrbuf, "text/css")) {
+ html->loadCssFromStash = false;
+ }
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) &&
+ dStrcasecmp(attrbuf, "all") && !dStristr(attrbuf, "screen")) {
+ /* HTML 4.01 sec. 6.13 says that media descriptors are case-sensitive,
+ * but sec. 14.2.3 says that the attribute is case-insensitive.
+ * TODO can be a comma-separated list.
+ * TODO handheld.
+ */
+ html->loadCssFromStash = false;
+ }
+
a_Html_stash_init(html);
S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
}
@@ -1696,8 +1711,9 @@ static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_close_style(DilloHtml *html, int TagIdx)
{
- /* eventually the stash will be sent to an interpreter for parsing */
- a_Html_pop_tag(html, TagIdx);
+ if (prefs.parse_embedded_css && html->loadCssFromStash)
+ html->styleEngine->parse(html, NULL, html->Stash->str, html->Stash->len,
+ CSS_ORIGIN_AUTHOR);
}
/*
@@ -1707,9 +1723,9 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
{
const char *attrbuf;
Textblock *textblock;
- StyleAttrs style_attrs;
- Style *style;
+ CssPropertyList props;
int32_t color;
+ int tag_index_a = a_Html_tag_index ("a");
if (!(html->InFlags & IN_BODY))
html->InFlags |= IN_BODY;
@@ -1724,43 +1740,53 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
BUG_MSG("unclosed HEAD element\n");
}
- textblock = DW2TB(html->dw);
+ textblock = HT2TB(html);
- if (!prefs.force_my_colors) {
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
- color = a_Html_color_parse(html, attrbuf, prefs.bg_color);
- if (color == 0xffffff && !prefs.allow_white_bg)
- color = prefs.bg_color;
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
+ color = a_Html_color_parse(html, attrbuf, -1);
+ if (color != -1)
+ props.set (CSS_PROPERTY_BACKGROUND_COLOR, CSS_TYPE_COLOR, color);
+ }
- style_attrs = *html->dw->getStyle ();
- style_attrs.backgroundColor = Color::createShaded(HT2LT(html), color);
- style = Style::create (HT2LT(html), &style_attrs);
- html->dw->setStyle (style);
- style->unref ();
- S_TOP(html)->current_bg_color = color;
- }
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "text"))) {
+ color = a_Html_color_parse(html, attrbuf, -1);
+ if (color != -1)
+ props.set (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR, color);
+ }
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "text"))) {
- color = a_Html_color_parse(html, attrbuf, prefs.text_color);
- HTML_SET_TOP_ATTR (html, color,
- Color::createSimple (HT2LT(html),color));
- }
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "link")))
+ html->non_css_link_color = a_Html_color_parse(html, attrbuf, -1);
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "link")))
- html->link_color = a_Html_color_parse(html,attrbuf,prefs.link_color);
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vlink")))
+ html->non_css_visited_color = a_Html_color_parse(html, attrbuf, -1);
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vlink")))
- html->visited_color = a_Html_color_parse(html, attrbuf,
- prefs.visited_color);
+ html->styleEngine->setNonCssHints (&props);
+ html->dw->setStyle (html->styleEngine->style ());
- if (prefs.contrast_visited_color) {
- /* get a color that has a "safe distance" from text, link and bg */
- html->visited_color =
- a_Color_vc(html->visited_color,
- S_TOP(html)->style->color->getColor(),
- html->link_color,
- S_TOP(html)->current_bg_color);
- }
+ /* Determine a color for visited links.
+ * This color is computed once per page and used for immediate feedback
+ * when clicking a link.
+ * On reload style including color for visited links is computed properly
+ * according to CSS.
+ */
+ html->styleEngine->startElement (tag_index_a);
+ html->styleEngine->setPseudoVisited ();
+ if (html->non_css_visited_color != -1) {
+ CssPropertyList vprops;
+ vprops.set (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR,
+ html->non_css_visited_color);
+ html->styleEngine->setNonCssHints (&vprops);
+ }
+ html->visited_color = html->styleEngine->style ()->color->getColor ();
+ html->styleEngine->endElement (tag_index_a);
+
+ if (prefs.contrast_visited_color) {
+ /* get a color that has a "safe distance" from text, link and bg */
+ html->visited_color =
+ a_Color_vc(html->visited_color,
+ html->styleEngine->style ()->color->getColor(),
+ html->non_css_link_color,
+ html->styleEngine->backgroundStyle()->backgroundColor->getColor());
}
S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_BODY;
@@ -1775,7 +1801,6 @@ static void Html_tag_close_body(DilloHtml *html, int TagIdx)
/* some tag soup pages use multiple BODY tags... */
html->InFlags &= ~IN_BODY;
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1785,13 +1810,12 @@ static void Html_tag_close_body(DilloHtml *html, int TagIdx)
*/
static void Html_tag_open_p(DilloHtml *html, const char *tag, int tagsize)
{
- if ((html->InFlags & IN_LI) && !html->WordAfterLI) {
- /* ignore first parbreak after an empty <LI> */
- html->WordAfterLI = true;
- } else {
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- }
- a_Html_tag_set_align_attr (html, tag, tagsize);
+ CssPropertyList props;
+
+ a_Html_tag_set_align_attr (html, &props, tag, tagsize);
+ html->styleEngine->inheritBackgroundColor ();
+ html->styleEngine->setNonCssHints (&props);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
/*
@@ -1805,11 +1829,10 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize)
char *src;
DilloUrl *url;
Textblock *textblock;
- StyleAttrs style_attrs;
- Style *link_style;
Widget *bullet;
+ CssPropertyList props;
- textblock = DW2TB(html->dw);
+ textblock = HT2TB(html);
if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "src")))
return;
@@ -1819,46 +1842,40 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize)
src = dStrdup(attrbuf);
- style_attrs = *(S_TOP(html)->style);
-
- if (a_Capi_get_flags(url) & CAPI_IsCached) { /* visited frame */
- style_attrs.color =
- Color::createSimple (HT2LT(html), html->visited_color);
- } else { /* unvisited frame */
- style_attrs.color = Color::createSimple (HT2LT(html), html->link_color);
+ if (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached) {
+ /* visited frame */
+ html->styleEngine->setPseudoVisited ();
+ } else {
+ /* unvisited frame */
+ html->styleEngine->setPseudoLink ();
}
- style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE;
- style_attrs.x_link = Html_set_new_link(html, &url);
- style_attrs.cursor = CURSOR_POINTER;
- link_style = Style::create (HT2LT(html), &style_attrs);
- textblock->addParbreak (5, S_TOP(html)->style);
+ props.set (PROPERTY_X_LINK, CSS_TYPE_INTEGER, Html_set_new_link(html,&url));
+ html->styleEngine->setNonCssHints (&props);
+
+ textblock->addParbreak (5, html->styleEngine->wordStyle ());
- /* The bullet will be assigned the current list style, which should
- * be "disc" by default, but may in very weird pages be different.
- * Anyway, there should be no harm. */
bullet = new Bullet();
- textblock->addWidget(bullet, S_TOP(html)->style);
- textblock->addSpace(S_TOP(html)->style);
+ textblock->addWidget(bullet, html->styleEngine->wordStyle ());
+ textblock->addSpace(html->styleEngine->wordStyle ());
if (tolower(tag[1]) == 'i') {
/* IFRAME usually comes with very long advertising/spying URLS,
* to not break rendering we will force name="IFRAME" */
- textblock->addText ("IFRAME", link_style);
+ textblock->addText ("IFRAME", html->styleEngine->wordStyle ());
} else {
/* FRAME:
* If 'name' tag is present use it, if not use 'src' value */
if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
- textblock->addText (src, link_style);
+ textblock->addText (src, html->styleEngine->wordStyle ());
} else {
- textblock->addText (attrbuf, link_style);
+ textblock->addText (attrbuf, html->styleEngine->wordStyle ());
}
}
- textblock->addParbreak (5, S_TOP(html)->style);
+ textblock->addParbreak (5, html->styleEngine->wordStyle ());
- link_style->unref ();
dFree(src);
}
@@ -1870,9 +1887,9 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_frameset (DilloHtml *html,
const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- DW2TB(html->dw)->addText("--FRAME--", S_TOP(html)->style);
- Html_add_indented(html, 40, 0, 5);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+ HT2TB(html)->addText("--FRAME--", html->styleEngine->wordStyle ());
+ Html_add_textblock(html, 5);
}
/*
@@ -1880,53 +1897,26 @@ static void Html_tag_open_frameset (DilloHtml *html,
*/
static void Html_tag_open_h(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
+ CssPropertyList props;
+
- /* TODO: combining these two would be slightly faster */
- a_Html_set_top_font(html, prefs.vw_fontname,
- Html_level_to_fontsize(FontSizesNum - (tag[2] - '0')),
- 1, 3);
- a_Html_tag_set_align_attr (html, tag, tagsize);
+ html->styleEngine->inheritBackgroundColor ();
+ a_Html_tag_set_align_attr (html, &props, tag, tagsize);
+ html->styleEngine->setNonCssHints (&props);
+
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
- /* First finalize unclosed H tags (we test if already named anyway) */
- a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
- a_Menu_pagemarks_add(html->bw, DW2TB(html->dw),
- S_TOP(html)->style, (tag[2] - '0'));
a_Html_stash_init(html);
S_TOP(html)->parse_mode =
DILLO_HTML_PARSE_MODE_STASH_AND_BODY;
}
/*
- * Handle close: <H1> | <H2> | <H3> | <H4> | <H5> | <H6>
- */
-static void Html_tag_close_h(DilloHtml *html, int TagIdx)
-{
- a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_pop_tag(html, TagIdx);
-}
-
-/*
- * <BIG> | <SMALL>
- */
-static void Html_tag_open_big_small(DilloHtml *html,
- const char *tag, int tagsize)
-{
- int level;
-
- level =
- Html_fontsize_to_level(S_TOP(html)->style->font->size) +
- ((dStrncasecmp(tag+1, "big", 3)) ? -1 : 1);
- a_Html_set_top_font(html, NULL, Html_level_to_fontsize(level), 0, 0);
-}
-
-/*
* <BR>
*/
static void Html_tag_open_br(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addLinebreak (S_TOP(html)->style);
+ HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
}
/*
@@ -1934,39 +1924,29 @@ static void Html_tag_open_br(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_font(DilloHtml *html, const char *tag, int tagsize)
{
- StyleAttrs style_attrs;
- Style *old_style;
- /*Font font;*/
const char *attrbuf;
+ char *fontFamily = NULL;
int32_t color;
+ CssPropertyList props;
- if (!prefs.force_my_colors) {
- old_style = S_TOP(html)->style;
- style_attrs = *old_style;
-
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "color"))) {
- if (prefs.contrast_visited_color && html->InVisitedLink) {
- color = html->visited_color;
- } else {
- /* use the tag-specified color */
- color = a_Html_color_parse(html, attrbuf,
- style_attrs.color->getColor());
- style_attrs.color = Color::createSimple (HT2LT(html), color);
- }
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "color"))) {
+ if (prefs.contrast_visited_color && html->InVisitedLink) {
+ color = html->visited_color;
+ } else {
+ /* use the tag-specified color */
+ color = a_Html_color_parse(html, attrbuf, -1);
}
+ if (color != -1)
+ props.set (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR, color);
+ }
-#if 0
- //if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "face"))) {
- // font = *( style_attrs.font );
- // font.name = attrbuf;
- // style_attrs.font = a_Dw_style_font_new_from_list (&font);
- //}
-#endif
-
- S_TOP(html)->style =
- Style::create (HT2LT(html), &style_attrs);
- old_style->unref ();
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "face"))) {
+ fontFamily = dStrdup(attrbuf);
+ props.set (CSS_PROPERTY_FONT_FAMILY, CSS_TYPE_SYMBOL, fontFamily);
}
+
+ html->styleEngine->setNonCssHints (&props);
+ dFree(fontFamily);
}
/*
@@ -1974,62 +1954,33 @@ static void Html_tag_open_font(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_abbr(DilloHtml *html, const char *tag, int tagsize)
{
-// DwTooltip *tooltip;
-// const char *attrbuf;
-//
-// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
-// tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
-// HTML_SET_TOP_ATTR(html, x_tooltip, tooltip);
-// }
-}
-
-/*
- * <B>
- */
-static void Html_tag_open_b(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 1, 1);
-}
-
-/*
- * <STRONG>
- */
-static void Html_tag_open_strong(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 1, 1);
-}
+ const char *attrbuf;
-/*
- * <I>
- */
-static void Html_tag_open_i(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 2, 2);
-}
+ if (prefs.show_tooltip &&
+ (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
+ CssPropertyList props;
+ char *tooltip_str = dStrdup(attrbuf);
-/*
- * <EM>
- */
-static void Html_tag_open_em(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 2, 2);
+ props.set (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, tooltip_str);
+ html->styleEngine->setNonCssHints (&props);
+ dFree(tooltip_str);
+ }
}
/*
- * <CITE>
+ * <CENTER>
*/
-static void Html_tag_open_cite(DilloHtml *html, const char *tag, int tagsize)
+static void Html_tag_open_center(DilloHtml *html, const char *tag, int tagsize)
{
- a_Html_set_top_font(html, NULL, 0, 2, 2);
+ HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
}
/*
- * <CENTER>
+ * </CENTER>, also used for </TABLE>
*/
-static void Html_tag_open_center(DilloHtml *html, const char *tag, int tagsize)
+static void Html_tag_close_center(DilloHtml *html, int TagIdx)
{
- DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style);
- HTML_SET_TOP_ATTR(html, textAlign, TEXT_ALIGN_CENTER);
+ HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
}
/*
@@ -2038,42 +1989,32 @@ static void Html_tag_open_center(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_address(DilloHtml *html,
const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_set_top_font(html, NULL, 0, 2, 2);
-}
-
-/*
- * <TT>
- */
-static void Html_tag_open_tt(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
/*
- * Read image-associated tag attributes,
- * create new image and add it to the html page (if add is TRUE).
+ * Read image-associated tag attributes and create new image.
*/
-DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag,
- int tagsize, DilloUrl *url,
- dw::core::style::StyleAttrs *style_attrs,
- bool add)
+DilloImage *a_Html_image_new(DilloHtml *html, const char *tag,
+ int tagsize, DilloUrl *url)
{
- const int MAX_W = 6000, MAX_H = 6000;
-
DilloImage *Image;
char *width_ptr, *height_ptr, *alt_ptr;
const char *attrbuf;
- Length l_w, l_h;
- int space, w = 0, h = 0;
+ CssLength l_w = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
+ CssLength l_h = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
+ int space, border, w = 0, h = 0;
bool load_now;
+ CssPropertyList props;
+ char *tooltip_str = NULL;
-// if (prefs.show_tooltip &&
-// (attrbuf = a_Html_get_attr(html, tag, tagsize, "title")))
-// style_attrs->x_tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
-
+ if (prefs.show_tooltip &&
+ (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
+ tooltip_str = dStrdup(attrbuf);
+ props.set (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, tooltip_str);
+ }
alt_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "alt", NULL);
- if ((!alt_ptr || !*alt_ptr) && !a_UIcmd_get_images_enabled(html->bw)) {
+ if ((!alt_ptr || !*alt_ptr) && !prefs.load_images) {
dFree(alt_ptr);
alt_ptr = dStrdup("[IMG]"); // Place holder for img_off mode
}
@@ -2083,17 +2024,35 @@ DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag,
// TODO: the same for percentage and relative lengths.
if (width_ptr) {
l_w = a_Html_parse_length (html, width_ptr);
- w = isAbsLength(l_w) ? absLengthVal(l_w) : 0;
+ w = (int) (CSS_LENGTH_TYPE(l_w) == CSS_LENGTH_TYPE_PX ?
+ CSS_LENGTH_VALUE(l_w) : 0);
}
if (height_ptr) {
l_h = a_Html_parse_length (html, height_ptr);
- h = isAbsLength(l_h) ? absLengthVal(l_h) : 0;
- }
- if (w < 0 || h < 0 || abs(w*h) > MAX_W * MAX_H) {
+ h = (int) (CSS_LENGTH_TYPE(l_h) == CSS_LENGTH_TYPE_PX ?
+ CSS_LENGTH_VALUE(l_h) : 0);
+ }
+ /* Check for suspicious image size request that would cause
+ * an excessive amount of memory to be allocated for the
+ * image buffer.
+ * Be careful to avoid integer overflows during the checks.
+ * There is an additional check in dw/image.cc to catch cases
+ * where only one dimension is given and the image is scaled
+ * preserving its original aspect ratio.
+ * Size requests passed via CSS are also checked there.
+ */
+ if (w < 0 || h < 0 ||
+ w > IMAGE_MAX_AREA || h > IMAGE_MAX_AREA ||
+ (h > 0 && w > IMAGE_MAX_AREA / h)) {
dFree(width_ptr);
dFree(height_ptr);
width_ptr = height_ptr = NULL;
- MSG("a_Html_add_new_image: suspicious image size request %dx%d\n", w, h);
+ MSG("a_Html_image_new: suspicious image size request %dx%d\n", w, h);
+ } else {
+ if (CSS_LENGTH_TYPE(l_w) != CSS_LENGTH_TYPE_AUTO)
+ props.set (CSS_PROPERTY_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE, l_w);
+ if (CSS_LENGTH_TYPE(l_h) != CSS_LENGTH_TYPE_AUTO)
+ props.set (CSS_PROPERTY_HEIGHT, CSS_TYPE_LENGTH_PERCENTAGE, l_h);
}
/* TODO: we should scale the image respecting its ratio.
@@ -2106,34 +2065,72 @@ DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag,
/* Spacing to the left and right */
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "hspace"))) {
space = strtol(attrbuf, NULL, 10);
- if (space > 0)
- style_attrs->margin.left = style_attrs->margin.right = space;
+ if (space > 0) {
+ space = CSS_CREATE_LENGTH(space, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_MARGIN_LEFT, CSS_TYPE_LENGTH_PERCENTAGE,
+ space);
+ props.set (CSS_PROPERTY_MARGIN_RIGHT, CSS_TYPE_LENGTH_PERCENTAGE,
+ space);
+ }
}
/* Spacing at the top and bottom */
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vspace"))) {
space = strtol(attrbuf, NULL, 10);
- if (space > 0)
- style_attrs->margin.top = style_attrs->margin.bottom = space;
+ if (space > 0) {
+ space = CSS_CREATE_LENGTH(space, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_MARGIN_TOP, CSS_TYPE_LENGTH_PERCENTAGE,
+ space);
+ props.set (CSS_PROPERTY_MARGIN_BOTTOM, CSS_TYPE_LENGTH_PERCENTAGE,
+ space);
+ }
+ }
+
+ /* Border */
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border"))) {
+ border = strtol(attrbuf, NULL, 10);
+ if (border >= 0) {
+ border = CSS_CREATE_LENGTH(border, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_BORDER_TOP_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ border);
+ props.set (CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, border);
+ props.set (CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, border);
+ props.set (CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, border);
+
+ props.set (CSS_PROPERTY_BORDER_TOP_STYLE, CSS_TYPE_ENUM,
+ BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_BOTTOM_STYLE, CSS_TYPE_ENUM,
+ BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_LEFT_STYLE, CSS_TYPE_ENUM,
+ BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_RIGHT_STYLE, CSS_TYPE_ENUM,
+ BORDER_SOLID);
+ }
}
/* x_img is an index to a list of {url,image} pairs.
- * We know Html_add_new_linkimage() will use size() as its next index */
- style_attrs->x_img = html->images->size();
+ * We know Html_add_new_htmlimage() will use size() as its next index */
+ props.set (PROPERTY_X_IMG, CSS_TYPE_INTEGER, html->images->size());
- /* Add a new image widget to this page */
- Image = a_Image_new(0, 0, alt_ptr, S_TOP(html)->current_bg_color);
- if (add) {
- Html_add_widget(html, (Widget*)Image->dw, width_ptr, height_ptr,
- style_attrs);
- }
+ html->styleEngine->setNonCssHints(&props);
- load_now = a_UIcmd_get_images_enabled(html->bw) ||
- (a_Capi_get_flags(url) & CAPI_IsCached);
- Html_add_new_linkimage(html, &url, load_now ? NULL : Image);
+ /* Add a new image widget to this page */
+ Image = a_Image_new(alt_ptr, 0);
+ if (HT2TB(html)->getBgColor())
+ Image->bg_color = HT2TB(html)->getBgColor()->getColor();
+
+ load_now = prefs.load_images ||
+ !dStrcasecmp(URL_SCHEME(url), "data") ||
+ (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached);
+ bool loading = false;
if (load_now)
- Html_load_image(html->bw, url, Image);
+ loading = Html_load_image(html->bw, url, html->page_url, Image);
+ Html_add_new_htmlimage(html, &url, loading ? NULL : Image);
+ dFree(tooltip_str);
dFree(width_ptr);
dFree(height_ptr);
dFree(alt_ptr);
@@ -2143,21 +2140,23 @@ DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag,
/*
* Tell cache to retrieve image
*/
-static void Html_load_image(BrowserWindow *bw, DilloUrl *url,
- DilloImage *Image)
+static bool Html_load_image(BrowserWindow *bw, DilloUrl *url,
+ const DilloUrl *requester, DilloImage *Image)
{
DilloWeb *Web;
int ClientKey;
/* Fill a Web structure for the cache query */
- Web = a_Web_new(url);
+ Web = a_Web_new(url, requester);
Web->bw = bw;
Web->Image = Image;
+ a_Image_ref(Image);
Web->flags |= WEB_Image;
/* Request image data from the cache */
if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
a_Bw_add_client(bw, ClientKey, 0);
a_Bw_add_url(bw, url);
}
+ return ClientKey != 0;
}
/*
@@ -2170,9 +2169,7 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize)
DilloImage *Image;
DilloUrl *url, *usemap_url;
Textblock *textblock;
- StyleAttrs style_attrs;
const char *attrbuf;
- int border;
/* This avoids loading images. Useful for viewing suspicious HTML email. */
if (URL_FLAGS(html->base_url) & URL_SpamSafe)
@@ -2182,41 +2179,21 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize)
!(url = a_Html_url_new(html, attrbuf, NULL, 0)))
return;
- textblock = DW2TB(html->dw);
+ textblock = HT2TB(html);
usemap_url = NULL;
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "usemap")))
/* TODO: usemap URLs outside of the document are not used. */
usemap_url = a_Html_url_new(html, attrbuf, NULL, 0);
- /* Set the style attributes for this image */
- style_attrs = *S_TOP(html)->style;
- if (S_TOP(html)->style->x_link != -1 ||
- usemap_url != NULL) {
- /* Images within links */
- border = 1;
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border")))
- border = strtol (attrbuf, NULL, 10);
-
- if (S_TOP(html)->style->x_link != -1) {
- /* In this case we can use the text color */
- style_attrs.setBorderColor (
- Color::createShaded (HT2LT(html), style_attrs.color->getColor()));
- } else {
- style_attrs.setBorderColor (
- Color::createShaded (HT2LT(html), html->link_color));
- }
- style_attrs.setBorderStyle (BORDER_SOLID);
- style_attrs.borderWidth.setVal (border);
- }
-
- Image = a_Html_add_new_image(html, tag, tagsize, url, &style_attrs, true);
+ Image = a_Html_image_new(html, tag, tagsize, url);
+ HT2TB(html)->addWidget((Widget*)Image->dw, html->styleEngine->style());
/* Image maps */
if (a_Html_get_attr(html, tag, tagsize, "ismap")) {
((::dw::Image*)Image->dw)->setIsMap();
_MSG(" Html_tag_open_img: server-side map (ISMAP)\n");
- } else if (S_TOP(html)->style->x_link != -1 &&
+ } else if (html->styleEngine->style ()->x_link != -1 &&
usemap_url == NULL) {
/* For simple links, we have to suppress the "image_pressed" signal.
* This is overridden for USEMAP images. */
@@ -2225,10 +2202,9 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize)
if (usemap_url) {
((::dw::Image*)Image->dw)->setUseMap(&html->maps,
- new ::object::String(usemap_url->url_string->str));
+ new ::object::String(URL_STR(usemap_url)));
a_Url_free (usemap_url);
}
- html->connectSignals((Widget*)Image->dw);
}
/*
@@ -2244,13 +2220,15 @@ static void Html_tag_open_map(DilloHtml *html, const char *tag, int tagsize)
BUG_MSG("nested <map>\n");
} else {
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
+ html->InFlags |= IN_MAP;
hash_name = dStrconcat("#", attrbuf, NULL);
url = a_Html_url_new(html, hash_name, NULL, 0);
- html->maps.startNewMap(new ::object::String(url->url_string->str));
+ html->maps.startNewMap(new ::object::String(URL_STR(url)));
a_Url_free (url);
dFree(hash_name);
+ } else {
+ BUG_MSG("name attribute is required for <map>\n");
}
- html->InFlags |= IN_MAP;
}
}
@@ -2259,8 +2237,18 @@ static void Html_tag_open_map(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_close_map(DilloHtml *html, int TagIdx)
{
+ /* This is a hack for the perhaps frivolous feature of drawing image map
+ * shapes when there is no image to display. If this map is defined after
+ * an image that has not been loaded (img != NULL), tell the image to
+ * redraw. (It will only do so if it uses a map.)
+ */
+ for (int i = 0; i < html->images->size(); i++) {
+ DilloImage *img = html->images->get(i)->image;
+
+ if (img)
+ ((dw::Image*) img->dw)->forceMapRedraw();
+ }
html->InFlags &= ~IN_MAP;
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -2306,7 +2294,7 @@ static void Html_tag_open_area(DilloHtml *html, const char *tag, int tagsize)
const char *attrbuf;
int link = -1;
Shape *shape = NULL;
-
+
if (!(html->InFlags & IN_MAP)) {
BUG_MSG("<area> element not inside <map>\n");
return;
@@ -2368,7 +2356,7 @@ static void Html_tag_open_area(DilloHtml *html, const char *tag, int tagsize)
dReturn_if_fail ( url != NULL );
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "alt")))
a_Url_set_alt(url, attrbuf);
-
+
link = Html_set_new_link(html, &url);
}
if (type == BACKGROUND)
@@ -2384,45 +2372,30 @@ static void Html_tag_open_area(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_object(DilloHtml *html, const char *tag, int tagsize)
{
- StyleAttrs style_attrs;
- Style *style;
DilloUrl *url, *base_url = NULL;
const char *attrbuf;
+ CssPropertyList props;
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "codebase"))) {
base_url = a_Html_url_new(html, attrbuf, NULL, 0);
}
-
+
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "data"))) {
url = a_Html_url_new(html, attrbuf,
URL_STR(base_url), (base_url != NULL));
dReturn_if_fail ( url != NULL );
- style_attrs = *S_TOP(html)->style;
-
- if (a_Capi_get_flags(url) & CAPI_IsCached) {
- style_attrs.color = Color::createSimple (
- HT2LT(html),
- html->visited_color
-/*
- a_Color_vc(html->visited_color,
- S_TOP(html)->style->color->getColor(),
- html->link_color,
- S_TOP(html)->style->backgroundColor->getColor()),
-*/
- );
+ if (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached) {
+ html->styleEngine->setPseudoVisited ();
} else {
- style_attrs.color = Color::createSimple(HT2LT(html),
- html->link_color);
+ html->styleEngine->setPseudoLink ();
}
- style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE;
- style_attrs.x_link = Html_set_new_link(html, &url);
- style_attrs.cursor = CURSOR_POINTER;
+ props.set(PROPERTY_X_LINK, CSS_TYPE_INTEGER,
+ Html_set_new_link(html, &url));
+ html->styleEngine->setNonCssHints (&props);
- style = Style::create (HT2LT(html), &style_attrs);
- DW2TB(html->dw)->addText("[OBJECT]", style);
- style->unref ();
+ HT2TB(html)->addText("[OBJECT]", html->styleEngine->wordStyle ());
}
a_Url_free(base_url);
}
@@ -2456,7 +2429,7 @@ static const char* Html_get_javascript_link(DilloHtml *html)
static void Html_add_anchor(DilloHtml *html, const char *name)
{
_MSG("Registering ANCHOR: %s\n", name);
- if (!DW2TB(html->dw)->addAnchor (name, S_TOP(html)->style))
+ if (!HT2TB(html)->addAnchor (name, html->styleEngine->style ()))
BUG_MSG("Anchor names must be unique within the document\n");
/*
* According to Sec. 12.2.1 of the HTML 4.01 spec, "anchor names that
@@ -2473,9 +2446,9 @@ static void Html_add_anchor(DilloHtml *html, const char *name)
*/
static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize)
{
- StyleAttrs style_attrs;
- Style *old_style;
DilloUrl *url;
+ char *tooltip_str = NULL;
+ CssPropertyList props;
const char *attrbuf;
/* TODO: add support for MAP with A HREF */
@@ -2490,44 +2463,51 @@ static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize)
url = a_Html_url_new(html, attrbuf, NULL, 0);
dReturn_if_fail ( url != NULL );
- old_style = S_TOP(html)->style;
- style_attrs = *old_style;
-
- if (a_Capi_get_flags(url) & CAPI_IsCached) {
+ if (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached) {
html->InVisitedLink = true;
- style_attrs.color = Color::createSimple (
- HT2LT(html),
- html->visited_color
-/*
- a_Color_vc(html->visited_color,
- S_TOP(html)->style->color->getColor(),
- html->link_color,
- S_TOP(html)->current_bg_color),
-*/
- );
+ html->styleEngine->setPseudoVisited ();
+ if (html->non_css_visited_color != -1)
+ props.set (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR,
+ html->non_css_visited_color);
} else {
- style_attrs.color = Color::createSimple(HT2LT(html),
- html->link_color);
+ html->styleEngine->setPseudoLink ();
+ if (html->non_css_link_color != -1)
+ props.set (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR,
+ html->non_css_link_color);
}
-// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "title")))
-// style_attrs.x_tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
-
- style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE;
- style_attrs.x_link = Html_set_new_link(html, &url);
- style_attrs.cursor = CURSOR_POINTER;
-
- S_TOP(html)->style =
- Style::create (HT2LT(html), &style_attrs);
- old_style->unref ();
+ props.set (PROPERTY_X_LINK, CSS_TYPE_INTEGER,
+ Html_set_new_link(html, &url));
+ }
+ if (prefs.show_tooltip &&
+ (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
+ tooltip_str = dStrdup(attrbuf);
+ props.set (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, tooltip_str);
}
+ html->styleEngine->setNonCssHints (&props);
+ dFree(tooltip_str);
+
+ html->styleEngine->inheritBackgroundColor ();
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
+ char *nameVal;
+ const char *id = html->styleEngine->getId ();
+
if (prefs.show_extra_warnings)
Html_check_name_val(html, attrbuf, "name");
- /* html->NameVal is freed in Html_process_tag */
- html->NameVal = a_Url_decode_hex_str(attrbuf);
- Html_add_anchor(html, html->NameVal);
+
+ nameVal = a_Url_decode_hex_str(attrbuf);
+
+ if (nameVal) {
+ /* We compare the "id" value with the url-decoded "name" value */
+ if (!id || strcmp(nameVal, id)) {
+ if (id)
+ BUG_MSG("'id' and 'name' attribute of <a> tag differ\n");
+ Html_add_anchor(html, nameVal);
+ }
+
+ dFree(nameVal);
+ }
}
}
@@ -2537,49 +2517,40 @@ static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_a(DilloHtml *html, int TagIdx)
{
html->InVisitedLink = false;
- a_Html_pop_tag(html, TagIdx);
}
/*
- * Insert underlined text in the page.
+ * <BLOCKQUOTE>
*/
-static void Html_tag_open_u(DilloHtml *html, const char *tag, int tagsize)
+static void Html_tag_open_blockquote(DilloHtml *html,
+ const char *tag, int tagsize)
{
- Style *style;
- StyleAttrs style_attrs;
-
- style = S_TOP(html)->style;
- style_attrs = *style;
- style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE;
- S_TOP(html)->style =
- Style::create (HT2LT(html), &style_attrs);
- style->unref ();
+ Html_add_textblock(html, 9);
}
/*
- * Insert strike-through text. Used by <S>, <STRIKE> and <DEL>.
+ * <Q>
*/
-static void Html_tag_open_strike(DilloHtml *html, const char *tag, int tagsize)
+static void Html_tag_open_q(DilloHtml *html, const char *tag, int tagsize)
{
- Style *style;
- StyleAttrs style_attrs;
+ /*
+ * Left Double Quotation Mark, which is wrong in many cases, but
+ * should at least be widely recognized.
+ */
+ const char *U201C = "\xe2\x80\x9c";
- style = S_TOP(html)->style;
- style_attrs = *style;
- style_attrs.textDecoration |= TEXT_DECORATION_LINE_THROUGH;
- S_TOP(html)->style =
- Style::create (HT2LT(html), &style_attrs);
- style->unref ();
+ HT2TB(html)->addText (U201C, html->styleEngine->wordStyle ());
}
/*
- * <BLOCKQUOTE>
+ * </Q>
*/
-static void Html_tag_open_blockquote(DilloHtml *html,
- const char *tag, int tagsize)
+static void Html_tag_close_q(DilloHtml *html, int TagIdx)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- Html_add_indented(html, 40, 40, 9);
+ /* Right Double Quotation Mark */
+ const char *U201D = "\xe2\x80\x9d";
+
+ HT2TB(html)->addText (U201D, html->styleEngine->wordStyle ());
}
/*
@@ -2590,47 +2561,27 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize)
const char *attrbuf;
ListStyleType list_style_type;
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- Html_add_indented(html, 40, 0, 9);
-
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) {
+ CssPropertyList props;
+
/* list_style_type explicitly defined */
- if (dStrncasecmp(attrbuf, "disc", 4) == 0)
+ if (dStrcasecmp(attrbuf, "disc") == 0)
list_style_type = LIST_STYLE_TYPE_DISC;
- else if (dStrncasecmp(attrbuf, "circle", 6) == 0)
+ else if (dStrcasecmp(attrbuf, "circle") == 0)
list_style_type = LIST_STYLE_TYPE_CIRCLE;
- else if (dStrncasecmp(attrbuf, "square", 6) == 0)
+ else if (dStrcasecmp(attrbuf, "square") == 0)
list_style_type = LIST_STYLE_TYPE_SQUARE;
else
/* invalid value */
list_style_type = LIST_STYLE_TYPE_DISC;
- } else {
- if (S_TOP(html)->list_type == HTML_LIST_UNORDERED) {
- /* Nested <UL>'s. */
- /* --EG :: I changed the behavior here : types are cycling instead of
- * being forced to square. It's easier for mixed lists level counting.
- */
- switch (S_TOP(html)->style->listStyleType) {
- case LIST_STYLE_TYPE_DISC:
- list_style_type = LIST_STYLE_TYPE_CIRCLE;
- break;
- case LIST_STYLE_TYPE_CIRCLE:
- list_style_type = LIST_STYLE_TYPE_SQUARE;
- break;
- case LIST_STYLE_TYPE_SQUARE:
- default: /* this is actually a bug */
- list_style_type = LIST_STYLE_TYPE_DISC;
- break;
- }
- } else {
- /* Either first <UL>, or a <OL> before. */
- list_style_type = LIST_STYLE_TYPE_DISC;
- }
+
+ props.set(CSS_PROPERTY_LIST_STYLE_TYPE, CSS_TYPE_ENUM, list_style_type);
+ html->styleEngine->setNonCssHints (&props);
}
- HTML_SET_TOP_ATTR(html, listStyleType, list_style_type);
- S_TOP(html)->list_type = HTML_LIST_UNORDERED;
+ Html_add_textblock(html, 9);
+ S_TOP(html)->list_type = HTML_LIST_UNORDERED;
S_TOP(html)->list_number = 0;
S_TOP(html)->ref_list_item = NULL;
}
@@ -2641,11 +2592,8 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_dir(DilloHtml *html, const char *tag, int tagsize)
{
- ListStyleType list_style_type = LIST_STYLE_TYPE_DISC;
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- Html_add_indented(html, 40, 0, 9);
- HTML_SET_TOP_ATTR(html, listStyleType, list_style_type);
S_TOP(html)->list_type = HTML_LIST_UNORDERED;
S_TOP(html)->list_number = 0;
S_TOP(html)->ref_list_item = NULL;
@@ -2668,28 +2616,29 @@ static void Html_tag_open_menu(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize)
{
const char *attrbuf;
- ListStyleType list_style_type;
int n = 1;
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- Html_add_indented(html, 40, 0, 9);
-
- list_style_type = LIST_STYLE_TYPE_DECIMAL;
-
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) {
+ CssPropertyList props;
+ ListStyleType listStyleType = LIST_STYLE_TYPE_DECIMAL;
+
if (*attrbuf == '1')
- list_style_type = LIST_STYLE_TYPE_DECIMAL;
+ listStyleType = LIST_STYLE_TYPE_DECIMAL;
else if (*attrbuf == 'a')
- list_style_type = LIST_STYLE_TYPE_LOWER_ALPHA;
+ listStyleType = LIST_STYLE_TYPE_LOWER_ALPHA;
else if (*attrbuf == 'A')
- list_style_type = LIST_STYLE_TYPE_UPPER_ALPHA;
+ listStyleType = LIST_STYLE_TYPE_UPPER_ALPHA;
else if (*attrbuf == 'i')
- list_style_type = LIST_STYLE_TYPE_LOWER_ROMAN;
+ listStyleType = LIST_STYLE_TYPE_LOWER_ROMAN;
else if (*attrbuf == 'I')
- list_style_type = LIST_STYLE_TYPE_UPPER_ROMAN;
+ listStyleType = LIST_STYLE_TYPE_UPPER_ROMAN;
+
+ props.set (CSS_PROPERTY_LIST_STYLE_TYPE, CSS_TYPE_ENUM, listStyleType);
+ html->styleEngine->setNonCssHints (&props);
}
- HTML_SET_TOP_ATTR(html, listStyleType, list_style_type);
+ Html_add_textblock(html, 9);
+
S_TOP(html)->list_type = HTML_LIST_ORDERED;
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "start")) &&
@@ -2706,59 +2655,45 @@ static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize)
{
- StyleAttrs style_attrs;
- Style *item_style, *word_style;
+ Style *style = html->styleEngine->style ();
+ Style *wordStyle = html->styleEngine->wordStyle ();
Widget **ref_list_item;
ListItem *list_item;
int *list_number;
const char *attrbuf;
char buf[16];
+ if (S_TOP(html)->list_type == HTML_LIST_NONE)
+ BUG_MSG("<li> outside <ul> or <ol>\n");
+
html->InFlags |= IN_LI;
- html->WordAfterLI = false;
/* Get our parent tag's variables (used as state storage) */
list_number = &html->stack->getRef(html->stack->size()-2)->list_number;
ref_list_item = &html->stack->getRef(html->stack->size()-2)->ref_list_item;
- /* set the item style */
- word_style = S_TOP(html)->style;
- style_attrs = *word_style;
- //style_attrs.backgroundColor = Color::createShaded (HT2LT(html), 0xffff40);
- //style_attrs.setBorderColor (Color::createSimple (HT2LT(html), 0x000000));
- //style_attrs.setBorderStyle (BORDER_SOLID);
- //style_attrs.borderWidth.setVal (1);
- item_style = Style::create (HT2LT(html), &style_attrs);
-
- DW2TB(html->dw)->addParbreak (2, word_style);
+ HT2TB(html)->addParbreak (0, wordStyle);
list_item = new ListItem ((ListItem*)*ref_list_item,prefs.limit_text_width);
- DW2TB(html->dw)->addWidget (list_item, item_style);
- DW2TB(html->dw)->addParbreak (2, word_style);
+ HT2TB(html)->addWidget (list_item, style);
+ HT2TB(html)->addParbreak (0, wordStyle);
*ref_list_item = list_item;
S_TOP(html)->textblock = html->dw = list_item;
- item_style->unref();
- /* Handle it when the user clicks on a link */
- html->connectSignals(list_item);
- switch (S_TOP(html)->list_type) {
- case HTML_LIST_ORDERED:
+ if (style->listStyleType == LIST_STYLE_TYPE_NONE) {
+ // none
+ } else if (style->listStyleType >= LIST_STYLE_TYPE_DECIMAL) {
+ // ordered
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "value")) &&
(*list_number = strtol(attrbuf, NULL, 10)) < 0) {
BUG_MSG("illegal negative LIST VALUE attribute; Starting from 0\n");
*list_number = 0;
}
- numtostr((*list_number)++, buf, 16, S_TOP(html)->style->listStyleType);
- list_item->initWithText (dStrdup(buf), word_style);
- list_item->addSpace (word_style);
- html->PrevWasSPC = true;
- break;
- case HTML_LIST_NONE:
- BUG_MSG("<li> outside <ul> or <ol>\n");
- default:
- list_item->initWithWidget (new Bullet(), word_style);
- list_item->addSpace (word_style);
- break;
+ numtostr((*list_number)++, buf, 16, style->listStyleType);
+ list_item->initWithText (buf, wordStyle);
+ } else {
+ // unordered
+ list_item->initWithWidget (new Bullet(), wordStyle);
}
}
@@ -2768,9 +2703,7 @@ static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_li(DilloHtml *html, int TagIdx)
{
html->InFlags &= ~IN_LI;
- html->WordAfterLI = false;
((ListItem *)html->dw)->flush ();
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -2779,58 +2712,55 @@ static void Html_tag_close_li(DilloHtml *html, int TagIdx)
static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize)
{
Widget *hruler;
- StyleAttrs style_attrs;
- Style *style;
+ CssPropertyList props;
char *width_ptr;
const char *attrbuf;
int32_t size = 0;
-
- style_attrs = *S_TOP(html)->style;
- width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", "100%");
- style_attrs.width = a_Html_parse_length (html, width_ptr);
- dFree(width_ptr);
+ width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", NULL);
+ if (width_ptr) {
+ props.set (CSS_PROPERTY_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ a_Html_parse_length (html, width_ptr));
+ dFree(width_ptr);
+ }
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size")))
size = strtol(attrbuf, NULL, 10);
-
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) {
- if (dStrcasecmp (attrbuf, "left") == 0)
- style_attrs.textAlign = TEXT_ALIGN_LEFT;
- else if (dStrcasecmp (attrbuf, "right") == 0)
- style_attrs.textAlign = TEXT_ALIGN_RIGHT;
- else if (dStrcasecmp (attrbuf, "center") == 0)
- style_attrs.textAlign = TEXT_ALIGN_CENTER;
- }
-
+
+ a_Html_tag_set_align_attr(html, &props, tag, tagsize);
+
/* TODO: evaluate attribute */
if (a_Html_get_attr(html, tag, tagsize, "noshade")) {
- style_attrs.setBorderStyle (BORDER_SOLID);
- style_attrs.setBorderColor (
- Color::createShaded (HT2LT(html), style_attrs.color->getColor()));
- if (size < 1)
+ props.set (CSS_PROPERTY_BORDER_TOP_STYLE, CSS_TYPE_ENUM, BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_BOTTOM_STYLE,CSS_TYPE_ENUM,BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_LEFT_STYLE, CSS_TYPE_ENUM, BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_RIGHT_STYLE, CSS_TYPE_ENUM, BORDER_SOLID);
+
+ if (size <= 0)
size = 1;
- } else {
- style_attrs.setBorderStyle (BORDER_INSET);
- style_attrs.setBorderColor
- (Color::createShaded (HT2LT(html),
- S_TOP(html)->current_bg_color));
- if (size < 2)
- size = 2;
- }
-
- style_attrs.borderWidth.top =
- style_attrs.borderWidth.left = (size + 1) / 2;
- style_attrs.borderWidth.bottom =
- style_attrs.borderWidth.right = size / 2;
- style = Style::create (HT2LT(html), &style_attrs);
-
- DW2TB(html->dw)->addParbreak (5, S_TOP(html)->style);
+ }
+
+ if (size > 0) {
+ CssLength size_top = CSS_CREATE_LENGTH ((size+1)/2, CSS_LENGTH_TYPE_PX);
+ CssLength size_bottom = CSS_CREATE_LENGTH (size / 2, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_BORDER_TOP_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ size_top);
+ props.set (CSS_PROPERTY_BORDER_LEFT_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ size_top);
+ props.set (CSS_PROPERTY_BORDER_BOTTOM_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ size_bottom);
+ props.set (CSS_PROPERTY_BORDER_RIGHT_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ size_bottom);
+ }
+
+ html->styleEngine->setNonCssHints (&props);
+
+ HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
+
hruler = new Ruler();
- hruler->setStyle (style);
- DW2TB(html->dw)->addWidget (hruler, style);
- style->unref ();
- DW2TB(html->dw)->addParbreak (5, S_TOP(html)->style);
+ hruler->setStyle (html->styleEngine->style ());
+ HT2TB(html)->addWidget (hruler, html->styleEngine->style ());
+ HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
}
/*
@@ -2839,7 +2769,7 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize)
{
/* may want to actually do some stuff here. */
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
/*
@@ -2847,8 +2777,7 @@ static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_dt(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_set_top_font(html, NULL, 0, 1, 1);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
/*
@@ -2856,8 +2785,7 @@ static void Html_tag_open_dt(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_dd(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- Html_add_indented(html, 40, 40, 9);
+ Html_add_textblock(html, 9);
}
/*
@@ -2865,14 +2793,8 @@ static void Html_tag_open_dd(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
- /* Is the placement of this statement right? */
- S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_PRE;
- HTML_SET_TOP_ATTR (html, whiteSpace, WHITE_SPACE_PRE);
- html->pre_column = 0;
- html->PreFirstChar = true;
html->InFlags |= IN_PRE;
}
@@ -2882,8 +2804,7 @@ static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_pre(DilloHtml *html, int TagIdx)
{
html->InFlags &= ~IN_PRE;
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_pop_tag(html, TagIdx);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
/*
@@ -2899,7 +2820,7 @@ static int Html_tag_pre_excludes(int tag_idx)
/* initialize array */
if (!ei_set[0])
for (i = 0; es_set[i]; ++i)
- ei_set[i] = Html_tag_index(es_set[i]);
+ ei_set[i] = a_Html_tag_index(es_set[i]);
for (i = 0; ei_set[i]; ++i)
if (tag_idx == ei_set[i])
@@ -2909,12 +2830,12 @@ static int Html_tag_pre_excludes(int tag_idx)
/*
* Handle <META>
- * We do not support http-equiv=refresh because it's non standard,
- * (the HTML 4.01 SPEC recommends explicitly to avoid it), and it
- * can be easily abused!
- *
+ * We do not support http-equiv=refresh with delay>0 because it's
+ * non standard, (the HTML 4.01 SPEC recommends explicitly to avoid it).
* More info at:
* http://lists.w3.org/Archives/Public/www-html/2000Feb/thread.html#msg232
+ * Instant client-side redirects (delay=0) are supported:
+ * http://www.w3.org/TR/2008/NOTE-WCAG20-TECHS-20081211/H76.html
*
* TODO: Note that we're sending custom HTML while still IN_HEAD. This
* is a hackish way to put the message. A much cleaner approach is to
@@ -2930,9 +2851,8 @@ static void Html_tag_open_meta(DilloHtml *html, const char *tag, int tagsize)
" <tr><td bgcolor='#a0a0a0' colspan='2'>The author wanted you to go\n"
" <a href='%s'>here</a>%s</td></tr></table><br>\n";
- const char *equiv, *content;
- char delay_str[64];
- Dstr *ds_msg;
+ const char *p, *equiv, *content, *new_content;
+ char delay_str[64], *mr_url;
int delay;
/* only valid inside HEAD */
@@ -2943,49 +2863,178 @@ static void Html_tag_open_meta(DilloHtml *html, const char *tag, int tagsize)
if ((equiv = a_Html_get_attr(html, tag, tagsize, "http-equiv"))) {
if (!dStrcasecmp(equiv, "refresh") &&
- (content = a_Html_get_attr(html, tag, tagsize, "content"))) {
+ (content = a_Html_get_attr(html, tag, tagsize, "content"))) {
/* Get delay, if present, and make a message with it */
- if ((delay = strtol(content, NULL, 0)))
+ if ((delay = strtol(content, NULL, 0))) {
snprintf(delay_str, 64, " after %d second%s.",
- delay, (delay > 1) ? "s" : "");
- else
+ delay, (delay > 1) ? "s" : "");
+ } else {
sprintf(delay_str, ".");
-
+ }
/* Skip to anything after "URL=" */
- while (*content && *(content++) != '=');
-
- /* Send a custom HTML message.
- * TODO: This is a hairy hack,
- * It'd be much better to build a widget. */
- ds_msg = dStr_sized_new(256);
- dStr_sprintf(ds_msg, meta_template, content, delay_str);
- {
- int SaveFlags = html->InFlags;
- html->InFlags = IN_BODY;
- html->TagSoup = false;
- Html_write_raw(html, ds_msg->str, ds_msg->len, 0);
- html->TagSoup = true;
- html->InFlags = SaveFlags;
+ while (*content && *(content++) != '=') ;
+ /* Handle the case of a quoted URL */
+ if (*content == '"' || *content == '\'') {
+ if ((p = strchr(content + 1, *content)))
+ mr_url = dStrndup(content + 1, p - content - 1);
+ else
+ mr_url = dStrdup(content + 1);
+ } else {
+ mr_url = dStrdup(content);
}
- dStr_free(ds_msg, 1);
+
+ if (delay == 0) {
+ /* zero-delay redirection */
+ html->stop_parser = true;
+ DilloUrl *new_url = a_Url_new(mr_url, URL_STR(html->base_url));
+ if (a_Capi_dpi_verify_request(html->bw, new_url))
+ a_UIcmd_redirection0((void*)html->bw, new_url);
+ a_Url_free(new_url);
+ } else {
+ /* Send a custom HTML message.
+ * TODO: This is a hairy hack,
+ * It'd be much better to build a widget. */
+ Dstr *ds_msg = dStr_sized_new(256);
+ dStr_sprintf(ds_msg, meta_template, mr_url, delay_str);
+ {
+ int o_InFlags = html->InFlags;
+ int o_TagSoup = html->TagSoup;
+ html->InFlags = IN_BODY;
+ html->TagSoup = false;
+ Html_write_raw(html, ds_msg->str, ds_msg->len, 0);
+ html->TagSoup = o_TagSoup;
+ html->InFlags = o_InFlags;
+ }
+ dStr_free(ds_msg, 1);
+ }
+ dFree(mr_url);
} else if (!dStrcasecmp(equiv, "content-type") &&
(content = a_Html_get_attr(html, tag, tagsize, "content"))) {
- if (a_Misc_content_type_cmp(html->content_type, content)) {
- const bool_t force = FALSE;
- const char *new_content =
- a_Capi_set_content_type(html->page_url, content, force);
- /* Cannot ask cache whether the content type was changed, as
- * this code in another bw might have already changed it for us.
- */
- if (a_Misc_content_type_cmp(html->content_type, new_content)) {
- a_Nav_repush(html->bw);
- html->stop_parser = true;
+ _MSG("Html_tag_open_meta: content={%s}\n", content);
+ /* Cannot ask cache whether the content type was changed, as
+ * this code in another bw might have already changed it for us.
+ */
+ new_content = a_Capi_set_content_type(html->page_url,content,"meta");
+ if (a_Misc_content_type_cmp(html->content_type, new_content)) {
+ html->stop_parser = true; /* The cache buffer is no longer valid */
+ a_UIcmd_repush(html->bw);
+ }
+ }
+ }
+}
+
+/*
+ * Called by the network engine when a stylesheet has new data.
+ */
+static void Html_css_load_callback(int Op, CacheClient_t *Client)
+{
+ _MSG("Html_css_load_callback: Op=%d\n", Op);
+ if (Op) { /* EOF */
+ BrowserWindow *bw = ((DilloWeb *)Client->Web)->bw;
+ /* Repush when we've got them all */
+ if (--bw->NumPendingStyleSheets == 0)
+ a_UIcmd_repush(bw);
+ }
+}
+
+/*
+ * Tell cache to retrieve a stylesheet
+ */
+void a_Html_load_stylesheet(DilloHtml *html, DilloUrl *url)
+{
+ char *data;
+ int len;
+
+ dReturn_if (url == NULL || ! prefs.load_stylesheets);
+
+ _MSG("Html_load_stylesheet: ");
+ if (a_Capi_get_buf(url, &data, &len)) {
+ _MSG("cached URL=%s len=%d", URL_STR(url), len);
+ if (a_Capi_get_flags_with_redirection(url) & CAPI_Completed) {
+ if (strncmp("@charset \"", data, 10) == 0) {
+ char *endq = strchr(data+10, '"');
+
+ if (endq && (endq - data <= 51)) {
+ /* IANA limits charset names to 40 characters */
+ const char *ignored;
+ char *content_type;
+
+ *endq = '\0';
+ content_type = dStrconcat("text/css; charset=", data+10, NULL);
+ *endq = '"';
+ a_Capi_unref_buf(url);
+ ignored = a_Capi_set_content_type(url, content_type, "meta");
+ dFree(content_type);
+ a_Capi_get_buf(url, &data, &len);
}
}
- }
+ html->styleEngine->parse(html, url, data, len, CSS_ORIGIN_AUTHOR);
+ }
+ a_Capi_unref_buf(url);
+ } else {
+ /* Fill a Web structure for the cache query */
+ int ClientKey;
+ DilloWeb *Web = a_Web_new(url, html->page_url);
+ Web->bw = html->bw;
+ if ((ClientKey = a_Capi_open_url(Web, Html_css_load_callback, NULL))) {
+ ++html->bw->NumPendingStyleSheets;
+ a_Bw_add_client(html->bw, ClientKey, 0);
+ a_Bw_add_url(html->bw, url);
+ MSG("NumPendingStyleSheets=%d", html->bw->NumPendingStyleSheets);
+ }
+ }
+ MSG("\n");
+}
+
+/*
+ * Parse the LINK element (Only CSS stylesheets by now).
+ * (If it either hits or misses, is not relevant here; that's up to the
+ * cache functions)
+ *
+ * TODO: How will we know when to use "handheld"? Ask the html->bw->ui for
+ * screen dimensions, or a dillorc preference.
+ */
+static void Html_tag_open_link(DilloHtml *html, const char *tag, int tagsize)
+{
+ DilloUrl *url;
+ const char *attrbuf;
+
+ //char *tag_str = dStrndup(tag, tagsize);
+ //MSG("Html_tag_open_link(): %s\n", tag_str);
+ //dFree(tag_str);
+
+ /* When viewing suspicious HTML email, don't load LINK */
+ dReturn_if (URL_FLAGS(html->base_url) & URL_SpamSafe);
+
+ /* Ignore LINK outside HEAD */
+ if (!(html->InFlags & IN_HEAD)) {
+ BUG_MSG("the LINK element must be inside the HEAD section\n");
+ return;
}
+ /* Remote stylesheets enabled? */
+ dReturn_if_fail (prefs.load_stylesheets);
+ /* CSS stylesheet link */
+ if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "rel")) ||
+ dStrcasecmp(attrbuf, "stylesheet"))
+ return;
+
+ /* IMPLIED attributes? */
+ if (((attrbuf = a_Html_get_attr(html, tag, tagsize, "type")) &&
+ dStrcasecmp(attrbuf, "text/css")) ||
+ ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) &&
+ !dStristr(attrbuf, "screen") && dStrcasecmp(attrbuf, "all")))
+ return;
+
+ if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "href")) ||
+ !(url = a_Html_url_new(html, attrbuf, NULL, 0)))
+ return;
+
+ MSG(" Html_tag_open_link(): addCssUrl %s\n", URL_STR(url));
+
+ html->addCssUrl(url);
+ a_Url_free(url);
}
/*
@@ -3031,60 +3080,9 @@ static void Html_tag_open_base(DilloHtml *html, const char *tag, int tagsize)
}
}
-/*
- * <CODE>
- */
-static void Html_tag_open_code(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
-}
-
-/*
- * <DFN>
- */
-static void Html_tag_open_dfn(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 2, 3);
-}
-
-/*
- * <KBD>
- */
-static void Html_tag_open_kbd(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
-}
-
-/*
- * <SAMP>
- */
-static void Html_tag_open_samp(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
-}
-
-/*
- * <VAR>
- */
-static void Html_tag_open_var(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 2, 2);
-}
-
-/*
- * <SUB>
- */
-static void Html_tag_open_sub(DilloHtml *html, const char *tag, int tagsize)
-{
- HTML_SET_TOP_ATTR (html, valign, VALIGN_SUB);
-}
-
-/*
- * <SUP>
- */
-static void Html_tag_open_sup(DilloHtml *html, const char *tag, int tagsize)
+static void Html_tag_open_default(DilloHtml *html,const char *tag,int tagsize)
{
- HTML_SET_TOP_ATTR (html, valign, VALIGN_SUPER);
+ html->styleEngine->inheritBackgroundColor();
}
/*
@@ -3092,25 +3090,18 @@ static void Html_tag_open_sup(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_div(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style);
- a_Html_tag_set_align_attr (html, tag, tagsize);
-}
+ CssPropertyList props;
-/*
- * </DIV>, also used for </TABLE> and </CENTER>
- */
-static void Html_tag_close_div(DilloHtml *html, int TagIdx)
-{
- DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style);
- a_Html_pop_tag(html, TagIdx);
+ a_Html_tag_set_align_attr (html, &props, tag, tagsize);
+ html->styleEngine->setNonCssHints (&props);
+ Html_add_textblock(html, 0);
}
/*
- * Default close for most tags - just pop the stack.
+ * Default close for most tags.
*/
static void Html_tag_close_default(DilloHtml *html, int TagIdx)
{
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -3118,8 +3109,7 @@ static void Html_tag_close_default(DilloHtml *html, int TagIdx)
*/
static void Html_tag_close_par(DilloHtml *html, int TagIdx)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_pop_tag(html, TagIdx);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
@@ -3158,55 +3148,56 @@ const TagInfo Tags[] = {
/* acronym 010101 */
{"address", B8(010110),'R',2, Html_tag_open_address, Html_tag_close_par},
{"area", B8(010001),'F',0, Html_tag_open_area, Html_tag_close_default},
- {"b", B8(010101),'R',2, Html_tag_open_b, Html_tag_close_default},
+ {"b", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
{"base", B8(100001),'F',0, Html_tag_open_base, Html_tag_close_default},
/* basefont 010001 */
/* bdo 010101 */
- {"big", B8(010101),'R',2, Html_tag_open_big_small, Html_tag_close_default},
- {"blockquote", B8(011110),'R',2,Html_tag_open_blockquote,Html_tag_close_par},
+ {"big", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"blockquote", B8(011110),'R',2, Html_tag_open_blockquote,
+ Html_tag_close_default},
{"body", B8(011110),'O',1, Html_tag_open_body, Html_tag_close_body},
{"br", B8(010001),'F',0, Html_tag_open_br, Html_tag_close_default},
{"button", B8(011101),'R',2, Html_tag_open_button, Html_tag_close_button},
/* caption */
- {"center", B8(011110),'R',2, Html_tag_open_center, Html_tag_close_div},
- {"cite", B8(010101),'R',2, Html_tag_open_cite, Html_tag_close_default},
- {"code", B8(010101),'R',2, Html_tag_open_code, Html_tag_close_default},
+ {"center", B8(011110),'R',2, Html_tag_open_center, Html_tag_close_center},
+ {"cite", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"code", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
/* col 010010 'F' */
/* colgroup */
- {"dd", B8(011110),'O',1, Html_tag_open_dd, Html_tag_close_par},
- {"del", B8(011101),'R',2, Html_tag_open_strike, Html_tag_close_default},
- {"dfn", B8(010101),'R',2, Html_tag_open_dfn, Html_tag_close_default},
+ {"dd", B8(011110),'O',1, Html_tag_open_dd, Html_tag_close_default},
+ {"del", B8(011101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"dfn", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
{"dir", B8(011010),'R',2, Html_tag_open_dir, Html_tag_close_par},
/* TODO: complete <div> support! */
- {"div", B8(011110),'R',2, Html_tag_open_div, Html_tag_close_div},
+ {"div", B8(011110),'R',2, Html_tag_open_div, Html_tag_close_default},
{"dl", B8(011010),'R',2, Html_tag_open_dl, Html_tag_close_par},
{"dt", B8(010110),'O',1, Html_tag_open_dt, Html_tag_close_par},
- {"em", B8(010101),'R',2, Html_tag_open_em, Html_tag_close_default},
+ {"em", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
/* fieldset */
{"font", B8(010101),'R',2, Html_tag_open_font, Html_tag_close_default},
{"form", B8(011110),'R',2, Html_tag_open_form, Html_tag_close_form},
{"frame", B8(010010),'F',0, Html_tag_open_frame, Html_tag_close_default},
{"frameset", B8(011110),'R',2,Html_tag_open_frameset, Html_tag_close_default},
- {"h1", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
- {"h2", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
- {"h3", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
- {"h4", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
- {"h5", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
- {"h6", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
+ {"h1", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
+ {"h2", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
+ {"h3", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
+ {"h4", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
+ {"h5", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
+ {"h6", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
{"head", B8(101101),'O',1, Html_tag_open_head, Html_tag_close_head},
{"hr", B8(010010),'F',0, Html_tag_open_hr, Html_tag_close_default},
{"html", B8(001110),'O',1, Html_tag_open_html, Html_tag_close_html},
- {"i", B8(010101),'R',2, Html_tag_open_i, Html_tag_close_default},
+ {"i", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
{"iframe", B8(011110),'R',2, Html_tag_open_frame, Html_tag_close_default},
{"img", B8(010001),'F',0, Html_tag_open_img, Html_tag_close_default},
{"input", B8(010001),'F',0, Html_tag_open_input, Html_tag_close_default},
/* ins */
{"isindex", B8(110001),'F',0, Html_tag_open_isindex, Html_tag_close_default},
- {"kbd", B8(010101),'R',2, Html_tag_open_kbd, Html_tag_close_default},
+ {"kbd", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
/* label 010101 */
/* legend 01?? */
{"li", B8(011110),'O',1, Html_tag_open_li, Html_tag_close_li},
- /* link 100000 'F' */
+ {"link", B8(100001),'F',0, Html_tag_open_link, Html_tag_close_default},
{"map", B8(011001),'R',2, Html_tag_open_map, Html_tag_close_map},
/* menu 1010 -- TODO: not exactly 1010, it can contain LI and inline */
{"menu", B8(011010),'R',2, Html_tag_open_menu, Html_tag_close_par},
@@ -3214,25 +3205,25 @@ const TagInfo Tags[] = {
/* noframes 1011 */
/* noscript 1011 */
{"object", B8(111101),'R',2, Html_tag_open_object, Html_tag_close_default},
- {"ol", B8(011010),'R',2, Html_tag_open_ol, Html_tag_close_par},
+ {"ol", B8(011010),'R',2, Html_tag_open_ol, Html_tag_close_default},
/* optgroup */
{"option", B8(010001),'O',1, Html_tag_open_option, Html_tag_close_default},
{"p", B8(010110),'O',1, Html_tag_open_p, Html_tag_close_par},
/* param 010001 'F' */
{"pre", B8(010110),'R',2, Html_tag_open_pre, Html_tag_close_pre},
- /* q 010101 */
- {"s", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
- {"samp", B8(010101),'R',2, Html_tag_open_samp, Html_tag_close_default},
+ {"q", B8(010101),'R',2, Html_tag_open_q, Html_tag_close_q},
+ {"s", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"samp", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
{"script", B8(111001),'R',2, Html_tag_open_script, Html_tag_close_script},
{"select", B8(010101),'R',2, Html_tag_open_select, Html_tag_close_select},
- {"small", B8(010101),'R',2, Html_tag_open_big_small, Html_tag_close_default},
- /* span 0101 */
- {"strike", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
- {"strong", B8(010101),'R',2, Html_tag_open_strong, Html_tag_close_default},
+ {"small", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"span", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"strike", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"strong", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
{"style", B8(100101),'R',2, Html_tag_open_style, Html_tag_close_style},
- {"sub", B8(010101),'R',2, Html_tag_open_sub, Html_tag_close_default},
- {"sup", B8(010101),'R',2, Html_tag_open_sup, Html_tag_close_default},
- {"table", B8(011010),'R',5, Html_tag_open_table, Html_tag_close_div},
+ {"sub", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"sup", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"table", B8(011010),'R',5, Html_tag_open_table, Html_tag_close_center},
/* tbody */
{"td", B8(011110),'O',3, Html_tag_open_td, Html_tag_close_default},
{"textarea", B8(010101),'R',2,Html_tag_open_textarea,Html_tag_close_textarea},
@@ -3241,10 +3232,10 @@ const TagInfo Tags[] = {
/* thead */
{"title", B8(100101),'R',2, Html_tag_open_title, Html_tag_close_title},
{"tr", B8(011010),'O',4, Html_tag_open_tr, Html_tag_close_default},
- {"tt", B8(010101),'R',2, Html_tag_open_tt, Html_tag_close_default},
- {"u", B8(010101),'R',2, Html_tag_open_u, Html_tag_close_default},
- {"ul", B8(011010),'R',2, Html_tag_open_ul, Html_tag_close_par},
- {"var", B8(010101),'R',2, Html_tag_open_var, Html_tag_close_default}
+ {"tt", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"u", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"ul", B8(011010),'R',2, Html_tag_open_ul, Html_tag_close_default},
+ {"var", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}
};
#define NTAGS (sizeof(Tags)/sizeof(Tags[0]))
@@ -3270,7 +3261,7 @@ static int Html_tag_compare(const char *p1, const char *p2)
* Get 'tag' index
* return -1 if tag is not handled yet
*/
-static int Html_tag_index(const char *tag)
+int a_Html_tag_index(const char *tag)
{
int low, high, mid, cond;
@@ -3301,17 +3292,17 @@ static int Html_needs_optional_close(int old_idx, int cur_idx)
if (i_P == -1) {
/* initialize the indexes of elements with optional close */
- i_P = Html_tag_index("p"),
- i_LI = Html_tag_index("li"),
- i_TD = Html_tag_index("td"),
- i_TR = Html_tag_index("tr"),
- i_TH = Html_tag_index("th"),
- i_DD = Html_tag_index("dd"),
- i_DT = Html_tag_index("dt"),
- i_OPTION = Html_tag_index("option");
- // i_THEAD = Html_tag_index("thead");
- // i_TFOOT = Html_tag_index("tfoot");
- // i_COLGROUP = Html_tag_index("colgroup");
+ i_P = a_Html_tag_index("p"),
+ i_LI = a_Html_tag_index("li"),
+ i_TD = a_Html_tag_index("td"),
+ i_TR = a_Html_tag_index("tr"),
+ i_TH = a_Html_tag_index("th"),
+ i_DD = a_Html_tag_index("dd"),
+ i_DT = a_Html_tag_index("dt"),
+ i_OPTION = a_Html_tag_index("option");
+ // i_THEAD = a_Html_tag_index("thead");
+ // i_TFOOT = a_Html_tag_index("tfoot");
+ // i_COLGROUP = a_Html_tag_index("colgroup");
}
if (old_idx == i_P || old_idx == i_DT) {
@@ -3408,10 +3399,11 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
if (!(html->InFlags & IN_HTML)) {
tag = "<html>";
- tag_idx = Html_tag_index(tag + 1);
+ tag_idx = a_Html_tag_index(tag + 1);
if (tag_idx != new_idx || IsCloseTag) {
/* implicit open */
Html_force_push_tag(html, tag_idx);
+ _MSG("Open : %*s%s\n", html->stack->size()," ",Tags[tag_idx].name);
Tags[tag_idx].open (html, tag, strlen(tag));
}
}
@@ -3420,10 +3412,11 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
/* head element */
if (!(html->InFlags & IN_HEAD)) {
tag = "<head>";
- tag_idx = Html_tag_index(tag + 1);
+ tag_idx = a_Html_tag_index(tag + 1);
if (tag_idx != new_idx || IsCloseTag) {
/* implicit open of the head element */
Html_force_push_tag(html, tag_idx);
+ _MSG("Open : %*s%s\n", html->stack->size()," ",Tags[tag_idx].name);
Tags[tag_idx].open (html, tag, strlen(tag));
}
}
@@ -3432,20 +3425,57 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
/* body element */
if (html->InFlags & IN_HEAD) {
tag = "</head>";
- tag_idx = Html_tag_index(tag + 2);
- Tags[tag_idx].close (html, tag_idx);
+ tag_idx = a_Html_tag_index(tag + 2);
+ Html_tag_cleanup_at_close(html, tag_idx);
}
tag = "<body>";
- tag_idx = Html_tag_index(tag + 1);
+ tag_idx = a_Html_tag_index(tag + 1);
if (tag_idx != new_idx || IsCloseTag) {
/* implicit open */
Html_force_push_tag(html, tag_idx);
+ _MSG("Open : %*s%s\n", html->stack->size()," ",Tags[tag_idx].name);
Tags[tag_idx].open (html, tag, strlen(tag));
}
}
}
/*
+ * Parse attributes that can appear on any tag.
+ */
+static void Html_parse_common_attrs(DilloHtml *html, char *tag, int tagsize)
+{
+ const char *attrbuf;
+
+ if (tagsize >= 8 && /* length of "<t id=i>" */
+ (attrbuf = Html_get_attr2(html, tag, tagsize, "id",
+ HTML_LeftTrim | HTML_RightTrim))) {
+ /* According to the SGML declaration of HTML 4, all NAME values
+ * occuring outside entities must be converted to uppercase
+ * (this is what "NAMECASE GENERAL YES" says). But the HTML 4
+ * spec states in Sec. 7.5.2 that anchor ids are case-sensitive.
+ * So we don't do it and hope for better specs in the future ...
+ */
+ Html_check_name_val(html, attrbuf, "id");
+
+ html->styleEngine->setId(attrbuf);
+ }
+
+ if (tagsize >= 11 && (prefs.parse_embedded_css || prefs.load_stylesheets)) {
+ /* length of "<t class=i>" or "<t style=i>" */
+ attrbuf = Html_get_attr2(html, tag, tagsize, "class",
+ HTML_LeftTrim | HTML_RightTrim);
+ if (attrbuf)
+ html->styleEngine->setClass (attrbuf);
+
+ attrbuf = Html_get_attr2(html, tag, tagsize, "style",
+ HTML_LeftTrim | HTML_RightTrim);
+ if (attrbuf)
+ html->styleEngine->setStyle (attrbuf);
+ }
+
+}
+
+/*
* Process a tag, given as 'tag' and 'tagsize'. -- tagsize is [1 based]
* ('tag' must include the enclosing angle brackets)
* This function calls the right open or close function for the tag.
@@ -3453,11 +3483,12 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
{
int ci, ni; /* current and new tag indexes */
- const char *attrbuf;
char *start = tag + 1; /* discard the '<' */
int IsCloseTag = (*start == '/');
- ni = Html_tag_index(start + IsCloseTag);
+ dReturn_if (html->stop_parser == true);
+
+ ni = a_Html_tag_index(start + IsCloseTag);
if (ni == -1) {
/* TODO: doctype parsing is a bit fuzzy, but enough for the time being */
if (!(html->InFlags & IN_HTML)) {
@@ -3491,40 +3522,30 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
/* Push the tag into the stack */
Html_push_tag(html, ni);
+ html->styleEngine->startElement (ni);
+ _MSG("Open : %*s%s\n", html->stack->size(), " ", Tags[ni].name);
+
+ /* Parse attributes that can appear on any tag */
+ Html_parse_common_attrs(html, tag, tagsize);
+
/* Call the open function for this tag */
+ _MSG("Open : %s\n", Tags[ni].name);
Tags[ni].open (html, tag, tagsize);
if (html->stop_parser)
break;
- /* Now parse attributes that can appear on any tag */
- if (tagsize >= 8 && /* length of "<t id=i>" */
- (attrbuf = Html_get_attr2(html, tag, tagsize, "id",
- HTML_LeftTrim | HTML_RightTrim))) {
- /* According to the SGML declaration of HTML 4, all NAME values
- * occuring outside entities must be converted to uppercase
- * (this is what "NAMECASE GENERAL YES" says). But the HTML 4
- * spec states in Sec. 7.5.2 that anchor ids are case-sensitive.
- * So we don't do it and hope for better specs in the future ...
- */
- Html_check_name_val(html, attrbuf, "id");
- /* We compare the "id" value with the url-decoded "name" value */
- if (!html->NameVal || strcmp(html->NameVal, attrbuf)) {
- if (html->NameVal)
- BUG_MSG("'id' and 'name' attribute of <a> tag differ\n");
- Html_add_anchor(html, attrbuf);
- }
+ if (S_TOP(html)->parse_mode != DILLO_HTML_PARSE_MODE_PRE &&
+ (html->styleEngine->style ()->whiteSpace == WHITE_SPACE_PRE ||
+ html->styleEngine->style ()->whiteSpace == WHITE_SPACE_PRE_WRAP)) {
+ S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_PRE;
+ html->pre_column = 0;
+ html->PreFirstChar = true;
}
- /* Reset NameVal */
- if (html->NameVal) {
- dFree(html->NameVal);
- html->NameVal = NULL;
- }
-
- /* let the parser know this was an open tag */
- html->PrevWasOpenTag = true;
+ if (html->styleEngine->getId ())
+ Html_add_anchor(html, html->styleEngine->getId ());
- /* Request inmediate close for elements with forbidden close tag. */
+ /* Request immediate close for elements with forbidden close tag. */
/* TODO: XHTML always requires close tags. A simple implementation
* of the commented clause below will make it work. */
if (/* parsing HTML && */ Tags[ni].EndTag == 'F')
@@ -3538,13 +3559,13 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
/* Test for </x>, ReqTagClose, <x /> and <x/> */
if (*start == '/' || /* </x> */
html->ReqTagClose || /* request */
- (tag[tagsize - 2] == '/' && /* XML: */
- (isspace(tag[tagsize - 3]) || /* <x /> */
+ (tag[tagsize-2] == '/' && /* XML: */
+ (strchr(" \"'", tag[tagsize-3]) || /* [ "']/> */
(size_t)tagsize == strlen(Tags[ni].name) + 3))) { /* <x/> */
-
- Tags[ni].close (html, ni);
+
+ _MSG("Close: %s\n", Tags[ni].name);
+ Html_tag_cleanup_at_close(html, ni);
/* This was a close tag */
- html->PrevWasOpenTag = false;
html->ReqTagClose = false;
}
}
@@ -3623,7 +3644,7 @@ static const char *Html_get_attr2(DilloHtml *html,
tagsize-i, &entsize)) >= 0) {
if (isocode >= 128) {
char buf[4];
- int k, n = utf8encode(isocode, buf);
+ int k, n = a_Utf8_encode(isocode, buf);
for (k = 0; k < n; ++k)
dStr_append_c(Buf, buf[k]);
} else {
@@ -3687,29 +3708,6 @@ char *a_Html_get_attr_wdef(DilloHtml *html,
}
/*
- * Add a widget to the page.
- */
-static void Html_add_widget(DilloHtml *html,
- Widget *widget,
- char *width_str,
- char *height_str,
- StyleAttrs *style_attrs)
-{
- StyleAttrs new_style_attrs;
- Style *style;
-
- new_style_attrs = *style_attrs;
- new_style_attrs.width = width_str ?
- a_Html_parse_length (html, width_str) : LENGTH_AUTO;
- new_style_attrs.height = height_str ?
- a_Html_parse_length (html, height_str) : LENGTH_AUTO;
- style = Style::create (HT2LT(html), &new_style_attrs);
- DW2TB(html->dw)->addWidget (widget, style);
- style->unref ();
-}
-
-
-/*
* Dispatch the apropriate function for 'Op'
* This function is a Cache client and gets called whenever new data arrives
* Op : operation to perform.
@@ -3739,13 +3737,13 @@ static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof)
Textblock *textblock;
int token_start, buf_index;
- dReturn_val_if_fail ((textblock = DW2TB(html->dw)) != NULL, 0);
+ dReturn_val_if_fail ((textblock = HT2TB(html)) != NULL, 0);
/* Now, 'buf' and 'bufsize' define a buffer aligned to start at a token
* boundary. Iterate through tokens until end of buffer is reached. */
buf_index = 0;
token_start = buf_index;
- while ((buf_index < bufsize) && (html->stop_parser == false)) {
+ while ((buf_index < bufsize) && !html->stop_parser) {
/* invariant: buf_index == bufsize || token_start == buf_index */
if (S_TOP(html)->parse_mode ==
@@ -3774,7 +3772,7 @@ static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof)
if (isspace(buf[buf_index])) {
/* whitespace: group all available whitespace */
- while (++buf_index < bufsize && isspace(buf[buf_index]));
+ while (++buf_index < bufsize && isspace(buf[buf_index])) ;
Html_process_space(html, buf + token_start, buf_index - token_start);
token_start = buf_index;
diff --git a/src/html.hh b/src/html.hh
index 3e08f37a..5b18c1a8 100644
--- a/src/html.hh
+++ b/src/html.hh
@@ -11,6 +11,9 @@ extern "C" {
* Exported functions
*/
void a_Html_load_images(void *v_html, DilloUrl *pattern);
+void a_Html_form_submit(void *v_html, void *v_form);
+void a_Html_form_reset(void *v_html, void *v_form);
+void a_Html_form_display_hiddens(void *v_html, void *v_form, bool_t display);
#ifdef __cplusplus
}
diff --git a/src/html_common.hh b/src/html_common.hh
index c694123e..3cca82de 100644
--- a/src/html_common.hh
+++ b/src/html_common.hh
@@ -13,12 +13,14 @@
#include "form.hh"
+#include "styleengine.hh"
+
/*
- * Macros
+ * Macros
*/
-// Dw to Textblock
-#define DW2TB(dw) ((Textblock*)dw)
+// "html struct" to Textblock
+#define HT2TB(html) ((Textblock*)(html->dw))
// "html struct" to "Layout"
#define HT2LT(html) ((Layout*)html->bw->render_layout)
// "Image" to "Dw Widget"
@@ -33,32 +35,15 @@
} D_STMT_END
/*
- * Change one toplevel attribute. var should be an identifier. val is
- * only evaluated once, so you can safely use a function call for it.
+ * Typedefs
*/
-#define HTML_SET_TOP_ATTR(html, var, val) \
- do { \
- StyleAttrs style_attrs; \
- Style *old_style; \
- \
- old_style = S_TOP(html)->style; \
- style_attrs = *old_style; \
- style_attrs.var = (val); \
- S_TOP(html)->style = \
- Style::create (HT2LT(html), &style_attrs); \
- old_style->unref (); \
- } while (FALSE)
-/*
- * Typedefs
- */
-
-typedef struct _DilloLinkImage DilloLinkImage;
+typedef struct _DilloHtmlImage DilloHtmlImage;
typedef struct _DilloHtmlState DilloHtmlState;
typedef enum {
- DT_NONE,
- DT_HTML,
+ DT_NONE,
+ DT_HTML,
DT_XHTML
} DilloHtmlDocumentType;
@@ -100,16 +85,16 @@ typedef enum {
} DilloHtmlProcessingState;
/*
- * Data Structures
+ * Data Structures
*/
-struct _DilloLinkImage {
+struct _DilloHtmlImage {
DilloUrl *url;
DilloImage *image;
};
struct _DilloHtmlState {
- dw::core::style::Style *style, *table_cell_style;
+ CssPropertyList *table_cell_props;
DilloHtmlParseMode parse_mode;
DilloHtmlTableMode table_mode;
bool cell_text_align_set;
@@ -124,10 +109,6 @@ struct _DilloHtmlState {
/* This is used to align list items (especially in enumerated lists) */
dw::core::Widget *ref_list_item;
- /* This makes image processing faster than a function
- a_Dw_widget_get_background_color. */
- int32_t current_bg_color;
-
/* This is used for list items etc; if it is set to TRUE, breaks
have to be "handed over" (see Html_add_indented and
Html_eventually_pop_dw). */
@@ -135,12 +116,12 @@ struct _DilloHtmlState {
};
/*
- * Classes
+ * Classes
*/
class DilloHtml {
private:
- class HtmlLinkReceiver: public dw::core::Widget::LinkReceiver {
+ class HtmlLinkReceiver: public dw::core::Layout::LinkReceiver {
public:
DilloHtml *html;
@@ -161,7 +142,6 @@ public: //BUG: for now everything is public
/* -------------------------------------------------------------------*/
/* Variables required at parsing time */
/* -------------------------------------------------------------------*/
- size_t Buf_Consumed; /* amount of source from cache consumed */
char *Start_Buf;
int Start_Ofs;
char *content_type, *charset;
@@ -173,7 +153,11 @@ public: //BUG: for now everything is public
DilloHtmlDocumentType DocType; /* as given by DOCTYPE tag */
float DocTypeVersion; /* HTML or XHTML version number */
+ /* vector of remote CSS resources, as given by the LINK element */
+ lout::misc::SimpleVector<DilloUrl*> *cssUrls;
+
lout::misc::SimpleVector<DilloHtmlState> *stack;
+ StyleEngine *styleEngine;
int InFlags; /* tracks which elements we are in */
@@ -184,33 +168,30 @@ public: //BUG: for now everything is public
bool PreFirstChar; /* used to skip the first CR or CRLF in PRE tags */
bool PrevWasCR; /* Flag to help parsing of "\r\n" in PRE tags */
bool PrevWasOpenTag; /* Flag to help deferred parsing of white space */
- bool PrevWasSPC; /* Flag to help handling collapsing white space */
bool InVisitedLink; /* used to 'contrast_visited_colors' */
bool ReqTagClose; /* Flag to help handling bad-formed HTML */
- bool CloseOneTag; /* Flag to help Html_tag_cleanup_at_close() */
- bool WordAfterLI; /* Flag to help ignoring the 1st <P> after <LI> */
bool TagSoup; /* Flag to enable the parser's cleanup functions */
- char *NameVal; /* used for validation of "NAME" and "ID" in <A> */
+ bool loadCssFromStash; /* current stash content should be loaded as CSS */
/* element counters: used for validation purposes */
uchar_t Num_HTML, Num_HEAD, Num_BODY, Num_TITLE;
Dstr *attr_data; /* Buffer for attribute value */
+ int32_t non_css_link_color; /* as provided by link attribute in BODY */
+ int32_t non_css_visited_color; /* as provided by vlink attribute in BODY */
+ int32_t visited_color; /* as computed according to CSS */
+
/* -------------------------------------------------------------------*/
/* Variables required after parsing (for page functionality) */
/* -------------------------------------------------------------------*/
lout::misc::SimpleVector<DilloHtmlForm*> *forms;
lout::misc::SimpleVector<DilloHtmlInput*> *inputs_outside_form;
lout::misc::SimpleVector<DilloUrl*> *links;
- lout::misc::SimpleVector<DilloLinkImage*> *images;
+ lout::misc::SimpleVector<DilloHtmlImage*> *images;
dw::ImageMapsList maps;
- int32_t link_color;
- int32_t visited_color;
-
private:
- bool parse_finished;
void freeParseData();
void initDw(); /* Used by the constructor */
@@ -227,12 +208,15 @@ public:
DilloHtmlForm *getCurrentForm ();
bool_t unloadedImages();
void loadImages (const DilloUrl *pattern);
+ void addCssUrl(const DilloUrl *url);
};
/*
- * Parser functions
+ * Parser functions
*/
+int a_Html_tag_index(const char *tag);
+
const char *a_Html_get_attr(DilloHtml *html,
const char *tag,
int tagsize,
@@ -248,10 +232,8 @@ DilloUrl *a_Html_url_new(DilloHtml *html,
const char *url_str, const char *base_url,
int use_base_url);
-DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag,
- int tagsize, DilloUrl *url,
- dw::core::style::StyleAttrs *style_attrs,
- bool add);
+DilloImage *a_Html_image_new(DilloHtml *html, const char *tag,
+ int tagsize, DilloUrl *url);
char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize);
void a_Html_pop_tag(DilloHtml *html, int TagIdx);
@@ -260,12 +242,12 @@ int32_t a_Html_color_parse(DilloHtml *html,
const char *subtag, int32_t default_color);
dw::core::style::Length a_Html_parse_length (DilloHtml *html,
const char *attr);
-void a_Html_tag_set_align_attr(DilloHtml *html,
+void a_Html_tag_set_align_attr(DilloHtml *html, CssPropertyList *props,
const char *tag, int tagsize);
bool a_Html_tag_set_valign_attr(DilloHtml *html,
const char *tag, int tagsize,
- dw::core::style::StyleAttrs *style_attrs);
-void a_Html_set_top_font(DilloHtml *html, const char *name, int size,
- int BI, int BImask);
+ CssPropertyList *props);
+
+void a_Html_load_stylesheet(DilloHtml *html, DilloUrl *url);
#endif /* __HTML_COMMON_HH__ */
diff --git a/src/image.cc b/src/image.cc
index 54eb4710..c499d977 100644
--- a/src/image.cc
+++ b/src/image.cc
@@ -15,9 +15,6 @@
* of data from an Image to a DwImage widget.
*/
-#include <stdio.h>
-#include <string.h>
-
#include "msg.h"
#include "image.hh"
@@ -27,23 +24,13 @@
using namespace dw::core;
// Image to Object-Image macro
-#define OI(Image) ((dw::Image*)(Image->dw))
-
-
-/*
- * Local data
- */
-static size_t linebuf_size = 0;
-static uchar_t *linebuf = NULL;
+#define I2DW(Image) ((dw::Image*)(Image->dw))
/*
* Create and initialize a new image structure.
*/
-DilloImage *a_Image_new(int width,
- int height,
- const char *alt_text,
- int32_t bg_color)
+DilloImage *a_Image_new(const char *alt_text, int32_t bg_color)
{
DilloImage *Image;
@@ -51,15 +38,12 @@ DilloImage *a_Image_new(int width,
Image->dw = (void*) new dw::Image(alt_text);
Image->width = 0;
Image->height = 0;
- Image->cmap = NULL;
- Image->in_type = DILLO_IMG_TYPE_NOTSET;
Image->bg_color = bg_color;
- Image->ProcessedBytes = 0;
Image->ScanNumber = 0;
Image->BitVec = NULL;
Image->State = IMG_Empty;
- Image->RefCount = 1;
+ Image->RefCount = 0;
return Image;
}
@@ -75,6 +59,7 @@ static void Image_free(DilloImage *Image)
/*
* Unref and free if necessary
+ * Do nothing if the argument is NULL
*/
void a_Image_unref(DilloImage *Image)
{
@@ -85,6 +70,7 @@ void a_Image_unref(DilloImage *Image)
/*
* Add a reference to an Image struct
+ * Do nothing if the argument is NULL
*/
void a_Image_ref(DilloImage *Image)
{
@@ -93,37 +79,6 @@ void a_Image_ref(DilloImage *Image)
}
/*
- * Decode 'buf' (an image line) into RGB format.
- */
-static uchar_t *
- Image_line(DilloImage *Image, const uchar_t *buf, const uchar_t *cmap, int y)
-{
- uint_t x;
-
- switch (Image->in_type) {
- case DILLO_IMG_TYPE_INDEXED:
- if (cmap) {
- for (x = 0; x < Image->width; x++)
- memcpy(linebuf + x * 3, cmap + buf[x] * 3, 3);
- } else {
- MSG("Gif:: WARNING, image lacks a color map\n");
- }
- break;
- case DILLO_IMG_TYPE_GRAY:
- for (x = 0; x < Image->width; x++)
- memset(linebuf + x * 3, buf[x], 3);
- break;
- case DILLO_IMG_TYPE_RGB:
- /* avoid a memcpy here! --Jcid */
- return (uchar_t *)buf;
- case DILLO_IMG_TYPE_NOTSET:
- MSG_ERR("Image_line: type not set...\n");
- break;
- }
- return linebuf;
-}
-
-/*
* Set initial parameters of the image
*/
void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
@@ -133,59 +88,27 @@ void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
_MSG("a_Image_set_parms: width=%d height=%d\n", width, height);
bool resize = (Image->width != width || Image->height != height);
- OI(Image)->setBuffer((Imgbuf*)v_imgbuf, resize);
+ I2DW(Image)->setBuffer((Imgbuf*)v_imgbuf, resize);
if (!Image->BitVec)
Image->BitVec = a_Bitvec_new(height);
- Image->in_type = type;
Image->width = width;
Image->height = height;
- if (3 * width > linebuf_size) {
- linebuf_size = 3 * width;
- linebuf = (uchar_t*) dRealloc(linebuf, linebuf_size);
- }
Image->State = IMG_SetParms;
}
/*
- * Reference the dicache entry color map
- */
-void a_Image_set_cmap(DilloImage *Image, const uchar_t *cmap)
-{
- Image->cmap = cmap;
- Image->State = IMG_SetCmap;
-}
-
-/*
- * Begin a new scan for a multiple-scan image
- */
-void a_Image_new_scan(DilloImage *Image, void *v_imgbuf)
-{
- a_Bitvec_clear(Image->BitVec);
- Image->ScanNumber++;
- ((Imgbuf*)v_imgbuf)->newScan();
-}
-
-/*
* Implement the write method
*/
-void a_Image_write(DilloImage *Image, void *v_imgbuf,
- const uchar_t *buf, uint_t y, int decode)
+void a_Image_write(DilloImage *Image, uint_t y)
{
- uchar_t *newbuf;
-
+ _MSG("a_Image_write\n");
dReturn_if_fail ( y < Image->height );
- if (decode) {
- /* Decode 'buf' and copy it into the DicEntry buffer */
- newbuf = Image_line(Image, buf, Image->cmap, y);
- ((Imgbuf*)v_imgbuf)->copyRow(y, (byte *)newbuf);
- }
+ /* Update the row in DwImage */
+ I2DW(Image)->drawRow(y);
a_Bitvec_set_bit(Image->BitVec, y);
Image->State = IMG_Write;
-
- /* Update the row in DwImage */
- OI(Image)->drawRow(y);
}
/*
@@ -193,46 +116,6 @@ void a_Image_write(DilloImage *Image, void *v_imgbuf,
*/
void a_Image_close(DilloImage *Image)
{
- a_Image_unref(Image);
-}
-
-
-// Wrappers for Imgbuf -------------------------------------------------------
-
-/*
- * Increment reference count for an Imgbuf
- */
-void a_Image_imgbuf_ref(void *v_imgbuf)
-{
- ((Imgbuf*)v_imgbuf)->ref();
-}
-
-/*
- * Decrement reference count for an Imgbuf
- */
-void a_Image_imgbuf_unref(void *v_imgbuf)
-{
- ((Imgbuf*)v_imgbuf)->unref();
-}
-
-/*
- * Create a new Imgbuf
- */
-void *a_Image_imgbuf_new(void *v_dw, int img_type, int width, int height)
-{
- Layout *layout = ((Widget*)v_dw)->getLayout();
- if (!layout) {
- MSG_ERR("a_Image_imgbuf_new: layout is NULL.\n");
- exit(1);
- }
- return (void*)layout->createImgbuf(Imgbuf::RGB, width, height);
-}
-
-/*
- * Last reference for this Imgbuf?
- */
-int a_Image_imgbuf_last_reference(void *v_imgbuf)
-{
- return ((Imgbuf*)v_imgbuf)->lastReference () ? 1 : 0;
+ _MSG("a_Image_close\n");
}
diff --git a/src/image.hh b/src/image.hh
index 73b0e5e7..a66edaae 100644
--- a/src/image.hh
+++ b/src/image.hh
@@ -12,12 +12,24 @@ extern "C" {
#include "bitvec.h"
#include "url.h"
+/*
+ * Defines
+ */
+
+/* Arbitrary maximum for image size (to avoid image size-crafting attacks). */
+#define IMAGE_MAX_AREA (6000 * 6000)
+
+/*
+ * Types
+ */
+
typedef struct _DilloImage DilloImage;
typedef enum {
DILLO_IMG_TYPE_INDEXED,
DILLO_IMG_TYPE_RGB,
DILLO_IMG_TYPE_GRAY,
+ DILLO_IMG_TYPE_CMYK_INV,
DILLO_IMG_TYPE_NOTSET /* Initial value */
} DilloImgType;
@@ -38,11 +50,7 @@ struct _DilloImage {
uint_t width;
uint_t height;
- const uchar_t *cmap; /* Color map (only for indexed) */
- DilloImgType in_type; /* Image Type */
int32_t bg_color; /* Background color */
-
- int ProcessedBytes; /* Amount of bytes already decoded */
bitvec_t *BitVec; /* Bit vector for decoded rows */
uint_t ScanNumber; /* Current decoding scan */
ImageState State; /* Processing status */
@@ -54,24 +62,16 @@ struct _DilloImage {
/*
* Function prototypes
*/
-DilloImage *a_Image_new(int width, int height,
- const char *alt_text, int32_t bg_color);
+DilloImage *a_Image_new(const char *alt_text, int32_t bg_color);
void a_Image_ref(DilloImage *Image);
void a_Image_unref(DilloImage *Image);
void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
int version, uint_t width, uint_t height,
DilloImgType type);
-void a_Image_set_cmap(DilloImage *Image, const uchar_t *cmap);
-void a_Image_new_scan(DilloImage *image, void *v_imgbuf);
-void a_Image_write(DilloImage *Image, void *v_imgbuf,
- const uchar_t *buf, uint_t y, int decode);
+void a_Image_write(DilloImage *Image, uint_t y);
void a_Image_close(DilloImage *Image);
-void a_Image_imgbuf_ref(void *v_imgbuf);
-void a_Image_imgbuf_unref(void *v_imgbuf);
-void *a_Image_imgbuf_new(void *v_dw, int img_type, int width, int height) ;
-int a_Image_imgbuf_last_reference(void *v_imgbuf);
#ifdef __cplusplus
}
diff --git a/src/imgbuf.cc b/src/imgbuf.cc
new file mode 100644
index 00000000..51f86b74
--- /dev/null
+++ b/src/imgbuf.cc
@@ -0,0 +1,138 @@
+/*
+ * File: imgbuf.cc
+ *
+ * Copyright (C) 2008 Jorge Arellano Cid <jcid@dillo.org>,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "msg.h"
+#include "imgbuf.hh"
+#include "dw/core.hh"
+#include "dw/image.hh"
+
+using namespace dw::core;
+
+/*
+ * Local data
+ */
+static size_t linebuf_size = 0;
+static uchar_t *linebuf = NULL;
+
+
+/*
+ * Decode 'buf' (an image line) into RGB format.
+ */
+static uchar_t *Imgbuf_rgb_line(const uchar_t *buf,
+ DilloImgType type, uchar_t *cmap,
+ uint_t width, uint_t y)
+{
+ uint_t x;
+
+ switch (type) {
+ case DILLO_IMG_TYPE_INDEXED:
+ if (cmap) {
+ for (x = 0; x < width; x++)
+ memcpy(linebuf + x * 3, cmap + buf[x] * 3, 3);
+ } else {
+ MSG_WARN("Gif:: image lacks a color map\n");
+ }
+ break;
+ case DILLO_IMG_TYPE_GRAY:
+ for (x = 0; x < width; x++)
+ memset(linebuf + x * 3, buf[x], 3);
+ break;
+ case DILLO_IMG_TYPE_CMYK_INV:
+ /*
+ * We treat CMYK as if it were "RGBW", and it works. Everyone who is
+ * trying to handle CMYK jpegs is confused by this, and supposedly
+ * the issue is that Adobe CMYK is "wrong" but ubiquitous.
+ */
+ for (x = 0; x < width; x++) {
+ uint_t white = buf[x * 4 + 3];
+ linebuf[x * 3] = buf[x * 4] * white / 0x100;
+ linebuf[x * 3 + 1] = buf[x * 4 + 1] * white / 0x100;
+ linebuf[x * 3 + 2] = buf[x * 4 + 2] * white / 0x100;
+ }
+ break;
+ case DILLO_IMG_TYPE_RGB:
+ /* avoid a memcpy here! --Jcid */
+ return (uchar_t *)buf;
+ case DILLO_IMG_TYPE_NOTSET:
+ MSG_ERR("Imgbuf_rgb_line: type not set...\n");
+ break;
+ }
+ return linebuf;
+}
+
+// Wrappers for Imgbuf -------------------------------------------------------
+
+/*
+ * Increment reference count for an Imgbuf
+ */
+void a_Imgbuf_ref(void *v_imgbuf)
+{
+ ((Imgbuf*)v_imgbuf)->ref();
+}
+
+/*
+ * Decrement reference count for an Imgbuf
+ */
+void a_Imgbuf_unref(void *v_imgbuf)
+{
+ if (v_imgbuf)
+ ((Imgbuf*)v_imgbuf)->unref();
+}
+
+/*
+ * Create a new Imgbuf
+ */
+void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height)
+{
+ Layout *layout = ((Widget*)v_dw)->getLayout();
+ if (!layout) {
+ MSG_ERR("a_Imgbuf_new: layout is NULL.\n");
+ exit(1);
+ }
+ // Assert linebuf is wide enough.
+ if (3 * width > linebuf_size) {
+ linebuf_size = 3 * width;
+ linebuf = (uchar_t*) dRealloc(linebuf, linebuf_size);
+ }
+
+ return (void*)layout->createImgbuf(Imgbuf::RGB, width, height);
+}
+
+/*
+ * Last reference for this Imgbuf?
+ */
+int a_Imgbuf_last_reference(void *v_imgbuf)
+{
+ return ((Imgbuf*)v_imgbuf)->lastReference () ? 1 : 0;
+}
+
+/*
+ * Update the root buffer of an imgbuf.
+ */
+void a_Imgbuf_update(void *v_imgbuf, const uchar_t *buf, DilloImgType type,
+ uchar_t *cmap, uint_t width, uint_t height, uint_t y)
+
+{
+ dReturn_if_fail ( y < height );
+
+ /* Decode 'buf' and copy it into the imgbuf */
+ uchar_t *newbuf = Imgbuf_rgb_line(buf, type, cmap, width, y);
+ ((Imgbuf*)v_imgbuf)->copyRow(y, (byte *)newbuf);
+}
+
+/*
+ * Reset for a new scan from a multiple-scan image.
+ */
+void a_Imgbuf_new_scan(void *v_imgbuf)
+{
+ ((Imgbuf*)v_imgbuf)->newScan();
+}
+
diff --git a/src/imgbuf.hh b/src/imgbuf.hh
new file mode 100644
index 00000000..9a6e3ff7
--- /dev/null
+++ b/src/imgbuf.hh
@@ -0,0 +1,30 @@
+#ifndef __IMGBUF_HH__
+#define __IMGBUF_HH__
+
+// Imgbuf wrappers
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#include "image.hh"
+
+/*
+ * Function prototypes
+ */
+void a_Imgbuf_ref(void *v_imgbuf);
+void a_Imgbuf_unref(void *v_imgbuf);
+void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height);
+int a_Imgbuf_last_reference(void *v_imgbuf);
+void a_Imgbuf_update(void *v_imgbuf, const uchar_t *buf, DilloImgType type,
+ uchar_t *cmap, uint_t width, uint_t height, uint_t y);
+void a_Imgbuf_new_scan(void *v_imgbuf);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __IMGBUF_HH__ */
+
diff --git a/src/jpeg.c b/src/jpeg.c
index 05e4f2d4..262a1346 100644
--- a/src/jpeg.c
+++ b/src/jpeg.c
@@ -32,7 +32,6 @@
#endif
#include "image.hh"
-#include "web.hh"
#include "cache.h"
#include "dicache.h"
#include "capi.h" /* get cache entry status */
@@ -81,57 +80,32 @@ typedef struct DilloJpeg {
/*
* Forward declarations
*/
-static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, int version);
-static void Jpeg_callback(int Op, CacheClient_t *Client);
static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize);
-static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client);
METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo);
-/* exported function */
-void *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call,
- void **Data);
-
/* this is the routine called by libjpeg when it detects an error. */
METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo)
{
/* display message and return to setjmp buffer */
my_error_ptr myerr = (my_error_ptr) cinfo->err;
- (*cinfo->err->output_message) (cinfo);
+ if (prefs.show_msg) {
+ DilloJpeg *jpeg =
+ ((my_source_mgr *) ((j_decompress_ptr) cinfo)->src)->jpeg;
+ MSG_WARN("\"%s\": ", URL_STR(jpeg->url));
+ (*cinfo->err->output_message) (cinfo);
+ }
longjmp(myerr->setjmp_buffer, 1);
}
/*
- * MIME handler for "image/jpeg" type
- * (Sets Jpeg_callback or a_Dicache_callback as the cache-client)
+ * Free the jpeg-decoding data structure.
*/
-void *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call,
- void **Data)
+static void Jpeg_free(DilloJpeg *jpeg)
{
- DilloWeb *web = P;
- DICacheEntry *DicEntry;
-
- if (!web->Image)
- web->Image = a_Image_new(0, 0, NULL, 0);
-
- /* Add an extra reference to the Image (for dicache usage) */
- a_Image_ref(web->Image);
-
- DicEntry = a_Dicache_get_entry(web->url);
- if (!DicEntry) {
- /* Let's create an entry for this image... */
- DicEntry = a_Dicache_add_entry(web->url);
-
- /* ... and let the decoder feed it! */
- *Data = Jpeg_new(web->Image, DicEntry->url, DicEntry->version);
- *Call = (CA_Callback_t) Jpeg_callback;
- } else {
- /* Let's feed our client from the dicache */
- a_Dicache_ref(DicEntry->url, DicEntry->version);
- *Data = web->Image;
- *Call = (CA_Callback_t) a_Dicache_callback;
- }
- return (web->Image->dw);
+ _MSG("Jpeg_free: jpeg=%p\n", jpeg);
+ jpeg_destroy_decompress(&(jpeg->cinfo));
+ dFree(jpeg);
}
/*
@@ -139,12 +113,17 @@ void *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call,
*/
static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client)
{
+ _MSG("Jpeg_close\n");
a_Dicache_close(jpeg->url, jpeg->version, Client);
- jpeg_destroy_decompress(&(jpeg->cinfo));
- dFree(jpeg);
+ Jpeg_free(jpeg);
}
-static void init_source(j_decompress_ptr cinfo)
+/*
+ * The proper signature is:
+ * static void init_source(j_decompress_ptr cinfo)
+ * (declaring it with no parameter avoids a compiler warning)
+ */
+static void init_source()
{
}
@@ -196,14 +175,20 @@ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
}
}
-static void term_source(j_decompress_ptr cinfo)
+/*
+ * The proper signature is:
+ * static void term_source(j_decompress_ptr cinfo)
+ * (declaring it with no parameter avoids a compiler warning)
+ */
+static void term_source()
{
}
-static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, int version)
+void *a_Jpeg_new(DilloImage *Image, DilloUrl *url, int version)
{
my_source_mgr *src;
DilloJpeg *jpeg = dMalloc(sizeof(*jpeg));
+ _MSG("a_Jpeg_new: jpeg=%p\n", jpeg);
jpeg->Image = Image;
jpeg->url = url;
@@ -236,12 +221,17 @@ static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, int version)
return jpeg;
}
-static void Jpeg_callback(int Op, CacheClient_t *Client)
+void a_Jpeg_callback(int Op, void *data)
{
- if (Op)
- Jpeg_close(Client->CbData, Client);
- else
+ if (Op == CA_Send) {
+ CacheClient_t *Client = data;
Jpeg_write(Client->CbData, Client->Buf, Client->BufSize);
+ } else if (Op == CA_Close) {
+ CacheClient_t *Client = data;
+ Jpeg_close(Client->CbData, Client);
+ } else if (Op == CA_Abort) {
+ Jpeg_free(data);
+ }
}
/*
@@ -278,14 +268,18 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
/* decompression step 3 (see libjpeg.doc) */
if (jpeg_read_header(&(jpeg->cinfo), TRUE) != JPEG_SUSPENDED) {
type = DILLO_IMG_TYPE_GRAY;
- if (jpeg->cinfo.num_components == 1)
+ if (jpeg->cinfo.num_components == 1) {
type = DILLO_IMG_TYPE_GRAY;
- else if (jpeg->cinfo.num_components == 3)
+ } else if (jpeg->cinfo.num_components == 3) {
type = DILLO_IMG_TYPE_RGB;
- else
- _MSG("jpeg: can't handle %d component images\n",
- jpeg->cinfo.num_components);
-
+ } else {
+ MSG("4-component JPEG!\n");
+ if (jpeg->cinfo.jpeg_color_space == JCS_YCCK)
+ MSG("YCCK. Are the colors wrong?\n");
+ if (!jpeg->cinfo.saw_Adobe_marker)
+ MSG("No adobe marker! Is the image shown in reverse video?\n");
+ type = DILLO_IMG_TYPE_CMYK_INV;
+ }
/*
* If a multiple-scan image is not completely in cache,
* use progressive display, updating as it arrives.
@@ -294,6 +288,17 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
!(a_Capi_get_flags(jpeg->url) & CAPI_Completed))
jpeg->cinfo.buffered_image = TRUE;
+ /* check max image size */
+ if (jpeg->cinfo.image_width <= 0 || jpeg->cinfo.image_height <= 0 ||
+ jpeg->cinfo.image_width >
+ IMAGE_MAX_AREA / jpeg->cinfo.image_height) {
+ MSG("Jpeg_write: suspicious image size request %ux%u\n",
+ (uint_t)jpeg->cinfo.image_width,
+ (uint_t)jpeg->cinfo.image_height);
+ jpeg->state = DILLO_JPEG_ERROR;
+ return;
+ }
+
a_Dicache_set_parms(jpeg->url, jpeg->version, jpeg->Image,
(uint_t)jpeg->cinfo.image_width,
(uint_t)jpeg->cinfo.image_height,
@@ -330,7 +335,7 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
if (jpeg->state == DILLO_JPEG_READ_BEGIN_SCAN) {
if (jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number)) {
- a_Dicache_new_scan(jpeg->Image, jpeg->url, jpeg->version);
+ a_Dicache_new_scan(jpeg->url, jpeg->version);
jpeg->state = DILLO_JPEG_READ_IN_SCAN;
}
}
@@ -346,8 +351,7 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
/* out of input */
break;
}
- a_Dicache_write(jpeg->Image, jpeg->url, jpeg->version,
- linebuf, jpeg->y);
+ a_Dicache_write(jpeg->url, jpeg->version, linebuf, jpeg->y);
jpeg->y++;
@@ -389,7 +393,7 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
/* out of input */
break;
}
- a_Dicache_new_scan(jpeg->Image, jpeg->url, jpeg->version);
+ a_Dicache_new_scan(jpeg->url, jpeg->version);
jpeg->state = DILLO_JPEG_READ_IN_SCAN;
}
}
@@ -398,4 +402,9 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
}
}
+#else /* ENABLE_JPEG */
+
+void *a_Jpeg_new() { return 0; }
+void a_Jpeg_callback() { return; }
+
#endif /* ENABLE_JPEG */
diff --git a/src/keys.cc b/src/keys.cc
new file mode 100644
index 00000000..1a39f4c8
--- /dev/null
+++ b/src/keys.cc
@@ -0,0 +1,363 @@
+/*
+ * Key parser
+ *
+ * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <fltk/events.h>
+#include <stdio.h>
+#include <stdlib.h> /* strtol */
+#include <string.h>
+#include <ctype.h>
+
+#include "dlib/dlib.h"
+#include "keys.hh"
+#include "msg.h"
+
+/*
+ * Local data types
+ */
+typedef struct {
+ const char *name;
+ KeysCommand_t cmd;
+ int modifier, key;
+} KeyBinding_t;
+
+typedef struct {
+ const char *name;
+ const int value;
+} Mapping_t;
+
+
+/*
+ * Local data
+ */
+static const Mapping_t keyNames[] = {
+ { "Backspace", fltk::BackSpaceKey },
+ { "Delete", fltk::DeleteKey },
+ { "Down", fltk::DownKey },
+ { "End", fltk::EndKey },
+ { "Esc", fltk::EscapeKey },
+ { "F1", fltk::F1Key },
+ { "F2", fltk::F2Key },
+ { "F3", fltk::F3Key },
+ { "F4", fltk::F4Key },
+ { "F5", fltk::F5Key },
+ { "F6", fltk::F6Key },
+ { "F7", fltk::F7Key },
+ { "F8", fltk::F8Key },
+ { "F9", fltk::F9Key },
+ { "F10", fltk::F10Key },
+ { "F11", fltk::F11Key },
+ { "F12", fltk::F12Key },
+ { "Home", fltk::HomeKey },
+ { "Insert", fltk::InsertKey },
+ { "Left", fltk::LeftKey },
+ { "PageDown", fltk::PageDownKey },
+ { "PageUp", fltk::PageUpKey },
+ { "Print", fltk::PrintKey },
+ { "Return", fltk::ReturnKey },
+ { "Right", fltk::RightKey },
+ { "Space", fltk::SpaceKey },
+ { "Tab", fltk::TabKey },
+ { "Up", fltk::UpKey }
+};
+
+static const Mapping_t modifierNames[] = {
+ { "Shift", fltk::SHIFT },
+ { "Ctrl", fltk::CTRL },
+ { "Alt", fltk::ALT },
+ { "Meta", fltk::META },
+ { "Button1", fltk::BUTTON1 },
+ { "Button2", fltk::BUTTON2 },
+ { "Button3", fltk::BUTTON3 }
+};
+
+static const KeyBinding_t default_keys[] = {
+ { "nop" , KEYS_NOP , 0 , 0 },
+ { "open" , KEYS_OPEN , fltk::CTRL , 'o' },
+ { "new-window" , KEYS_NEW_WINDOW , fltk::CTRL , 'n' },
+ { "new-tab" , KEYS_NEW_TAB , fltk::CTRL , 't' },
+ { "left-tab" , KEYS_LEFT_TAB , fltk::SHIFT , fltk::TabKey },
+ { "right-tab" , KEYS_RIGHT_TAB , fltk::CTRL , fltk::TabKey },
+ { "close-tab" , KEYS_CLOSE_TAB , fltk::CTRL , 'q' },
+ { "find" , KEYS_FIND , fltk::CTRL , 'f' },
+ { "websearch" , KEYS_WEBSEARCH , fltk::CTRL , 's' },
+ { "bookmarks" , KEYS_BOOKMARKS , fltk::CTRL , 'b' },
+ { "fullscreen" , KEYS_FULLSCREEN , fltk::CTRL , fltk::SpaceKey },
+ { "reload" , KEYS_RELOAD , fltk::CTRL , 'r' },
+ { "stop" , KEYS_STOP , 0 , 0 },
+ { "save" , KEYS_SAVE , 0 , 0 },
+ { "hide-panels" , KEYS_HIDE_PANELS , 0 , fltk::EscapeKey },
+ { "file-menu" , KEYS_FILE_MENU , fltk::ALT , 'f' },
+ { "close-all" , KEYS_CLOSE_ALL , fltk::ALT , 'q' },
+ { "back" , KEYS_BACK , 0 , fltk::BackSpaceKey },
+ { "back" , KEYS_BACK , 0 , ',' },
+ { "forward" , KEYS_FORWARD , fltk::SHIFT , fltk::BackSpaceKey },
+ { "forward" , KEYS_FORWARD , 0 , '.' },
+ { "goto" , KEYS_GOTO , fltk::CTRL , 'l' },
+ { "home" , KEYS_HOME , fltk::CTRL , 'h' },
+ { "screen-up" , KEYS_SCREEN_UP , 0 , fltk::PageUpKey },
+ { "screen-up" , KEYS_SCREEN_UP , 0 , 'b' },
+ { "screen-down" , KEYS_SCREEN_DOWN , 0 , fltk::PageDownKey },
+ { "screen-down" , KEYS_SCREEN_DOWN , 0 , fltk::SpaceKey },
+ { "line-up" , KEYS_LINE_UP , 0 , fltk::UpKey },
+ { "line-down" , KEYS_LINE_DOWN , 0 , fltk::DownKey },
+ { "left" , KEYS_LEFT , 0 , fltk::LeftKey },
+ { "right" , KEYS_RIGHT , 0 , fltk::RightKey },
+ { "top" , KEYS_TOP , 0 , fltk::HomeKey },
+ { "bottom" , KEYS_BOTTOM , 0 , fltk::EndKey },
+};
+
+static Dlist *bindings;
+
+
+
+/*
+ * Initialize the bindings list
+ */
+void Keys::init()
+{
+ KeyBinding_t *node;
+
+ // Fill our key bindings list
+ bindings = dList_new(32);
+ for (uint_t i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) {
+ if (default_keys[i].key) {
+ node = dNew(KeyBinding_t, 1);
+ node->name = dStrdup(default_keys[i].name);
+ node->cmd = default_keys[i].cmd;
+ node->modifier = default_keys[i].modifier;
+ node->key = default_keys[i].key;
+ dList_insert_sorted(bindings, node, nodeByKeyCmp);
+ }
+ }
+}
+
+/*
+ * Free data
+ */
+void Keys::free()
+{
+ KeyBinding_t *node;
+
+ while ((node = (KeyBinding_t*)dList_nth_data(bindings, 0))) {
+ dFree((char*)node->name);
+ dList_remove_fast(bindings, node);
+ dFree(node);
+ }
+ dList_free(bindings);
+}
+
+/*
+ * Compare function by {key,modifier} pairs.
+ */
+int Keys::nodeByKeyCmp(const void *node, const void *key)
+{
+ KeyBinding_t *n = (KeyBinding_t*)node, *k = (KeyBinding_t*)key;
+ _MSG("Keys::nodeByKeyCmp modifier=%d\n", k->modifier);
+ return (n->key != k->key) ? (n->key - k->key) : (n->modifier - k->modifier);
+}
+
+/*
+ * Look if the just pressed key is bound to a command.
+ * Return value: The command if found, KEYS_NOP otherwise.
+ */
+KeysCommand_t Keys::getKeyCmd()
+{
+ KeysCommand_t ret = KEYS_NOP;
+ KeyBinding_t keyNode;
+ // We're only interested in some flags
+ keyNode.modifier = fltk::event_state() &
+ (fltk::SHIFT | fltk::CTRL | fltk::ALT | fltk::META);
+
+ if (keyNode.modifier == fltk::SHIFT &&
+ ispunct(fltk::event_text()[0])) {
+ // Get key code for a shifted character
+ keyNode.key = fltk::event_text()[0];
+ keyNode.modifier = 0;
+ } else {
+ keyNode.key = fltk::event_key();
+ }
+
+ _MSG("getKeyCmd: key=%d, mod=%d\n", keyNode.key, keyNode.modifier);
+ void *data = dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
+ if (data)
+ ret = ((KeyBinding_t*)data)->cmd;
+ return ret;
+}
+
+/*
+ * Remove a key binding from the table.
+ */
+void Keys::delKeyCmd(int key, int mod)
+{
+ KeyBinding_t keyNode, *node;
+ keyNode.key = key;
+ keyNode.modifier = mod;
+
+ node = (KeyBinding_t*) dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
+ if (node) {
+ dList_remove(bindings, node);
+ dFree((char*)node->name);
+ dFree(node);
+ }
+}
+
+/*
+ * Takes a key name and looks it up in the mapping table. If
+ * found, its key code is returned. Otherwise -1 is given
+ * back.
+ */
+int Keys::getKeyCode(char *keyName)
+{
+ uint_t i;
+ for (i = 0; i < sizeof(keyNames) / sizeof(Mapping_t); i++) {
+ if (!dStrcasecmp(keyNames[i].name, keyName)) {
+ return keyNames[i].value;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Takes a command name and searches it in the mapping table.
+ * Return value: command code if found, -1 otherwise
+ */
+KeysCommand_t Keys::getCmdCode(const char *commandName)
+{
+ uint_t i;
+
+ for (i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) {
+ if (!dStrcasecmp(default_keys[i].name, commandName))
+ return default_keys[i].cmd;
+ }
+ return KEYS_INVALID;
+}
+
+/*
+ * Takes a modifier name and looks it up in the mapping table. If
+ * found, its key code is returned. Otherwise -1 is given back.
+ */
+int Keys::getModifier(char *modifierName)
+{
+ uint_t i;
+ for (i = 0; i < sizeof(modifierNames) / sizeof(Mapping_t); i++) {
+ if (!dStrcasecmp(modifierNames[i].name, modifierName)) {
+ return modifierNames[i].value;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Given a keys command, return a shortcut for it, or 0 if there is none
+ * (e.g., for KEYS_NEW_WINDOW, return CTRL+'n').
+ */
+int Keys::getShortcut(KeysCommand_t cmd)
+{
+ int len = dList_length(bindings);
+
+ for (int i = 0; i < len; i++) {
+ KeyBinding_t *node = (KeyBinding_t*)dList_nth_data(bindings, i);
+ if (cmd == node->cmd)
+ return node->modifier + node->key;
+ }
+ return 0;
+}
+
+/*
+ * Parse a key-combination/command-name pair, and
+ * insert it into the bindings list.
+ */
+void Keys::parseKey(char *key, char *commandName)
+{
+ char *p, *modstr, *keystr;
+ KeysCommand_t symcode;
+ int st, keymod = 0, keycode = 0;
+
+ _MSG("Keys::parseKey key='%s' commandName='%s'\n", key, commandName);
+
+ // Get command code
+ if ((symcode = getCmdCode(commandName)) == KEYS_INVALID) {
+ MSG("Keys::parseKey: Invalid command name: '%s'\n", commandName);
+ return;
+ }
+
+ // Skip space
+ for ( ; isspace(*key); ++key) ;
+ // Get modifiers
+ while(*key == '<' && (p = strchr(key, '>'))) {
+ ++key;
+ modstr = dStrndup(key, p - key);
+ if ((st = getModifier(modstr)) == -1) {
+ MSG("Keys::parseKey unknown modifier: %s\n", modstr);
+ } else {
+ keymod |= st;
+ }
+ dFree(modstr);
+ key = p + 1;
+ }
+ // Allow trailing space after keyname
+ keystr = (*key && (p = strchr(key + 1, ' '))) ? dStrndup(key, p - key - 1) :
+ dStrdup(key);
+ // Get key code
+ if (!key[1]) {
+ keycode = *key;
+ } else if (key[0] == '0' && key[1] == 'x') {
+ /* keysym. For details on values reported, see fltk's fltk/events.h */
+ keycode = strtol(key, NULL, 0x10);
+ } else if ((st = getKeyCode(keystr)) == -1) {
+ MSG("Keys::parseKey unknown keyname: %s\n", keystr);
+ } else {
+ keycode = st;
+ }
+ dFree(keystr);
+
+ // Set binding
+ if (keycode) {
+ delKeyCmd(keycode, keymod);
+ if (symcode != KEYS_NOP) {
+ KeyBinding_t *node = dNew(KeyBinding_t, 1);
+ node->name = dStrdup(commandName);
+ node->cmd = symcode;
+ node->modifier = keymod;
+ node->key = keycode;
+ dList_insert_sorted(bindings, node, nodeByKeyCmp);
+ _MSG("parseKey: Adding key=%d, mod=%d\n", node->key, node->modifier);
+ }
+ }
+}
+
+/*
+ * Parse the keysrc.
+ */
+void Keys::parse(FILE *fp)
+{
+ char *line, *keycomb, *command;
+ int st, lineno = 1;
+
+ // scan the file line by line
+ while ((line = dGetline(fp)) != NULL) {
+ st = dParser_parse_rc_line(&line, &keycomb, &command);
+
+ if (st == 0) {
+ _MSG("Keys::parse: keycomb=%s, command=%s\n", keycomb, command);
+ parseKey(keycomb, command);
+ } else if (st < 0) {
+ MSG("Keys::parse: Syntax error in keysrc line %d: "
+ "keycomb=\"%s\" command=\"%s\"\n", lineno, keycomb, command);
+ }
+
+ dFree(line);
+ ++lineno;
+ }
+ fclose(fp);
+}
diff --git a/src/keys.hh b/src/keys.hh
new file mode 100644
index 00000000..d234838c
--- /dev/null
+++ b/src/keys.hh
@@ -0,0 +1,68 @@
+/*
+ * Key parser
+ *
+ * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __KEYS_HH__
+#define __KEYS_HH__
+
+
+typedef enum {
+ KEYS_INVALID = -1,
+ KEYS_NOP, /* No operation bound */
+ KEYS_OPEN,
+ KEYS_NEW_WINDOW,
+ KEYS_NEW_TAB,
+ KEYS_LEFT_TAB,
+ KEYS_RIGHT_TAB,
+ KEYS_CLOSE_TAB,
+ KEYS_FIRST_TAB,
+ KEYS_LAST_TAB,
+ KEYS_FIND,
+ KEYS_WEBSEARCH,
+ KEYS_BOOKMARKS,
+ KEYS_FULLSCREEN,
+ KEYS_RELOAD,
+ KEYS_STOP,
+ KEYS_SAVE,
+ KEYS_HIDE_PANELS,
+ KEYS_FILE_MENU,
+ KEYS_CLOSE_ALL,
+ KEYS_BACK,
+ KEYS_FORWARD,
+ KEYS_GOTO,
+ KEYS_HOME,
+ KEYS_SCREEN_UP,
+ KEYS_SCREEN_DOWN,
+ KEYS_LINE_UP,
+ KEYS_LINE_DOWN,
+ KEYS_LEFT,
+ KEYS_RIGHT,
+ KEYS_TOP,
+ KEYS_BOTTOM
+} KeysCommand_t;
+
+class Keys {
+private:
+ static int nodeByKeyCmp(const void *node, const void *key);
+ static void delKeyCmd(int key, int mod);
+ static KeysCommand_t getCmdCode(const char *symbolName);
+ static int getKeyCode(char *keyName);
+ static int getModifier(char *modifierName);
+ static void parseKey(char *key, char *symbol);
+public:
+ static void init();
+ static void free();
+ static void parse(FILE *fp);
+ static KeysCommand_t getKeyCmd(void);
+ static int getShortcut(KeysCommand_t cmd);
+};
+
+
+#endif /* __KEYS_HH__ */
diff --git a/src/keysrc b/src/keysrc
new file mode 100644
index 00000000..3f53674f
--- /dev/null
+++ b/src/keysrc
@@ -0,0 +1,101 @@
+# keysrc
+# Sample dillo key bindings file.
+#
+# The format is: "key = action" or "<modifier>key = action".
+# Lines that begin with a '#' are comments.
+# The commented-out bindings below show the defaults built into Dillo.
+#
+# Modifiers recognized: "Shift", "Ctrl", "Alt", "Meta".
+# Key names recognized: "Backspace", "Delete", "Down", "End", "Esc",
+# "F1" through "F12", "Home", "Insert", "Left", "PageDown", "PageUp",
+# "Print", "Return", "Right", "Space", "Tab", "Up".
+#
+# If Dillo is running under X11, keys whose names are not recognized can
+# be specified using their keysym value in hexadecimal. Use xev to get
+# the keysym. Example rule: "0x1008ff27 = forward".
+#
+# The action "nop" (no operation) can be used to remove a binding.
+
+# "open" lets you browse your local files for one to open.
+#<ctrl>o = open
+
+# "new-window" opens a new browser window.
+#<ctrl>n = new-window
+
+# "new-tab" opens a new tab in the current browser window.
+#<ctrl>t = new-tab
+
+# "close-tab" closes the current tab.
+# Note that this closes the browser window if there is only one tab.
+#<ctrl>q = close-tab
+
+# "close-all" closes all tabs/windows and exits.
+#<alt>q = close-all
+
+# "left-tab" and "right-tab" switch to the left/right of the current tab.
+# *** NOT HOOKED UP YET ***
+# <shift>tab = left-tab
+# <ctrl>tab = right-tab
+
+# "back" and "forward" move back/forward through the browser history.
+#backspace = back
+#<shift>backspace = forward
+#, = back
+#. = forward
+
+# "reload" the current page.
+#<ctrl>r = reload
+
+# "home" goes to the homepage that you set in your dillorc.
+#<ctrl>h = home
+
+# "find" lets you search for a text string on the current page.
+#<ctrl>f = find
+
+# "hide-panels" hides the findbar.
+#esc = hide-panels
+
+# "websearch" lets you send a text string to the search engine that you
+# set in your dillorc.
+#<ctrl>s = websearch
+
+# go to your "bookmarks".
+#<ctrl>b = bookmarks
+
+# "fullscreen" hides/shows the panels at the top and bottom of a dillo window.
+#<ctrl>space = fullscreen
+
+# "file-menu" pops up the file menu.
+#<alt>f = file-menu
+
+# "goto" goes to the location bar at the top of the window.
+#<ctrl>l = goto
+
+# "stop" loading the page.
+#(stop has no default binding)
+
+# "save" the current page.
+#(save has no default binding)
+
+#--------------------------------------------------------------------
+# MOTION COMMANDS
+#--------------------------------------------------------------------
+
+#pageup = screen-up
+#b = screen-up
+
+#pagedown = screen-down
+#space = screen-down
+
+#up = line-up
+
+#down = line-down
+
+#left = left
+
+#right = right
+
+#home = top
+
+#end = bottom
+
diff --git a/src/klist.c b/src/klist.c
index a0652499..813269a3 100644
--- a/src/klist.c
+++ b/src/klist.c
@@ -117,9 +117,10 @@ void a_Klist_free(Klist_t **KlistPtr)
while (dList_length(Klist->List) > 0) {
node = dList_nth_data(Klist->List, 0);
- dList_remove_fast(Klist->List, 0);
+ dList_remove_fast(Klist->List, node);
dFree(node);
}
+ dList_free(Klist->List);
dFree(Klist);
*KlistPtr = NULL;
}
diff --git a/src/menu.cc b/src/menu.cc
index 6856143e..d802bddb 100644
--- a/src/menu.cc
+++ b/src/menu.cc
@@ -11,19 +11,20 @@
// Functions/Methods for menus
-#include <stdio.h>
-#include <stdarg.h>
#include <fltk/events.h>
#include <fltk/PopupMenu.h>
#include <fltk/Item.h>
+#include <fltk/ToggleItem.h>
#include <fltk/Divider.h>
+#include "lout/misc.hh" /* SimpleVector */
#include "msg.h"
#include "menu.hh"
#include "uicmd.hh"
#include "history.h"
#include "html.hh"
#include "ui.hh" // for (UI *)
+#include "keys.hh"
#include "timeout.hh"
using namespace fltk;
@@ -38,9 +39,9 @@ static DilloUrl *popup_url = NULL;
// Weak reference to the popup's bw
static BrowserWindow *popup_bw = NULL;
// Where to place the filemenu popup
-int popup_x, popup_y;
+static int popup_x, popup_y;
// History popup direction (-1 = back, 1 = forward).
-static int history_direction = -1;
+static int history_direction = -1;
// History popup, list of URL-indexes.
static int *history_list = NULL;
@@ -66,7 +67,7 @@ public:
* TODO: erase the URL on popup close.
*/
void CustItem::draw() {
- DilloUrl *url;
+ const DilloUrl *url;
if (flags() & SELECTED) {
url = a_History_get_url(history_list[(VOIDP2INT(user_data()))-1]);
@@ -84,7 +85,7 @@ static void filemenu_cb(Widget *wid, void *data)
{
if (strcmp((char*)data, "nw") == 0) {
UI *ui = (UI*)popup_bw->ui;
- a_UIcmd_browser_window_new(ui->w(), ui->h(), popup_bw);
+ a_UIcmd_browser_window_new(ui->w(), ui->h(), 0, popup_bw);
} else if (strcmp((char*)data, "nt") == 0) {
a_UIcmd_open_url_nt(popup_bw, NULL, 1);
} else if (strcmp((char*)data, "of") == 0) {
@@ -108,22 +109,22 @@ static void Menu_copy_urlstr_cb(Widget *)
static void Menu_link_cb(Widget*, void *user_data)
{
DilloUrl *url = (DilloUrl *) user_data ;
- MSG("Menu_link_cb: click! :-)\n");
+ _MSG("Menu_link_cb: click! :-)\n");
if (url)
a_Menu_link_popup(popup_bw, url);
}
-/*
+/*
* Open URL
*/
static void Menu_open_url_cb(Widget* )
{
- MSG("Open URL cb: click! :-)\n");
+ _MSG("Open URL cb: click! :-)\n");
a_UIcmd_open_url(popup_bw, popup_url);
}
-/*
+/*
* Open URL in new window
*/
static void Menu_open_url_nw_cb(Widget* )
@@ -132,7 +133,7 @@ static void Menu_open_url_nw_cb(Widget* )
a_UIcmd_open_url_nw(popup_bw, popup_url);
}
-/*
+/*
* Open URL in new Tab
*/
static void Menu_open_url_nt_cb(Widget* )
@@ -142,7 +143,7 @@ static void Menu_open_url_nt_cb(Widget* )
a_UIcmd_open_url_nt(popup_bw, popup_url, focus);
}
-/*
+/*
* Add bookmark
*/
static void Menu_add_bookmark_cb(Widget* )
@@ -150,7 +151,7 @@ static void Menu_add_bookmark_cb(Widget* )
a_UIcmd_add_bookmark(popup_bw, popup_url);
}
-/*
+/*
* Find text
*/
static void Menu_find_text_cb(Widget* )
@@ -158,7 +159,7 @@ static void Menu_find_text_cb(Widget* )
((UI *)popup_bw->ui)->set_findbar_visibility(1);
}
-/*
+/*
* Save link
*/
static void Menu_save_link_cb(Widget* )
@@ -166,7 +167,7 @@ static void Menu_save_link_cb(Widget* )
a_UIcmd_save_link(popup_bw, popup_url);
}
-/*
+/*
* Save current page
*/
static void Menu_save_page_cb(Widget* )
@@ -174,15 +175,15 @@ static void Menu_save_page_cb(Widget* )
a_UIcmd_save(popup_bw);
}
-/*
- * Save current page
+/*
+ * View current page source
*/
static void Menu_view_page_source_cb(Widget* )
{
- a_UIcmd_view_page_source(popup_url);
+ a_UIcmd_view_page_source(popup_bw, popup_url);
}
-/*
+/*
* View current page's bugs
*/
static void Menu_view_page_bugs_cb(Widget* )
@@ -192,23 +193,58 @@ static void Menu_view_page_bugs_cb(Widget* )
/*
* Load images on current page that match URL pattern
- *
- * BUG: assumes that the document is a DilloHtml.
*/
static void Menu_load_images_cb(Widget*, void *user_data)
{
- DilloUrl *pattern = (DilloUrl *) user_data ;
+ DilloUrl *page_url = (DilloUrl *) user_data;
+ void *doc = a_Bw_get_url_doc(popup_bw, page_url);
- if (popup_bw && popup_bw->Docs) {
- int i, n;
- n = dList_length(popup_bw->Docs);
- for (i = 0; i < n; i++) {
- a_Html_load_images(dList_nth_data(popup_bw->Docs, i), pattern);
- }
- }
+ if (doc)
+ a_Html_load_images(doc, popup_url);
+}
+
+/*
+ * Submit form
+ */
+static void Menu_form_submit_cb(Widget*, void *v_form)
+{
+ void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
+
+ if (doc)
+ a_Html_form_submit(doc, v_form);
+}
+
+/*
+ * Reset form
+ */
+static void Menu_form_reset_cb(Widget*, void *v_form)
+{
+ void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
+
+ if (doc)
+ a_Html_form_reset(doc, v_form);
+}
+
+/*
+ * Toggle display of 'hidden' form controls.
+ */
+static void Menu_form_hiddens_cb(Widget *w, void *user_data)
+{
+ void *v_form = w->parent()->user_data();
+ bool visible = *((bool *) user_data);
+ void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
+
+ if (doc)
+ a_Html_form_display_hiddens(doc, v_form, !visible);
+}
+
+static void Menu_stylesheet_cb(Widget *w, void *vUrl)
+{
+ const DilloUrl *url = (const DilloUrl *) vUrl;
+ a_UIcmd_open_url(popup_bw, url);
}
-/*
+/*
* Validate URL with the W3C
*/
static void Menu_bugmeter_validate_w3c_cb(Widget* )
@@ -221,7 +257,7 @@ static void Menu_bugmeter_validate_w3c_cb(Widget* )
dStr_free(dstr, 1);
}
-/*
+/*
* Validate URL with the WDG
*/
static void Menu_bugmeter_validate_wdg_cb(Widget* )
@@ -235,7 +271,7 @@ static void Menu_bugmeter_validate_wdg_cb(Widget* )
dStr_free(dstr, 1);
}
-/*
+/*
* Show info page for the bug meter
*/
static void Menu_bugmeter_about_cb(Widget* )
@@ -251,7 +287,7 @@ static void Menu_history_cb(Widget *wid, void *data)
{
int mb = ((CustItem*)wid)->button();
int offset = history_direction * VOIDP2INT(data);
- DilloUrl *url = a_History_get_url(history_list[VOIDP2INT(data)-1]);
+ const DilloUrl *url = a_History_get_url(history_list[VOIDP2INT(data)-1]);
if (mb == 2) {
// Middle button, open in a new window/tab
@@ -293,38 +329,41 @@ static void Menu_popup_cb2(void *data)
/*
* Page popup menu (construction & popup)
*/
-void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
- bool_t has_bugs, bool_t unloaded_imgs)
+void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
+ bool_t has_bugs, void *v_cssUrls)
{
+ lout::misc::SimpleVector <DilloUrl*> *cssUrls =
+ (lout::misc::SimpleVector <DilloUrl*> *) v_cssUrls;
+ Item *i;
+ int j;
// One menu for every browser window
static PopupMenu *pm = 0;
// Active/inactive control.
- static Item *view_page_bugs_item = 0;
- static Item *load_images_item = 0;
+ static Item *view_page_bugs_item = 0, *view_source_item = 0;
+ static ItemGroup *stylesheets = 0;
popup_bw = bw;
a_Url_free(popup_url);
popup_url = a_Url_dup(url);
if (!pm) {
- Item *i;
pm = new PopupMenu(0,0,0,0,"&PAGE OPTIONS");
pm->begin();
- i = new Item("View page Source");
+ i = view_source_item = new Item("View page Source");
i->callback(Menu_view_page_source_cb);
i = view_page_bugs_item = new Item("View page Bugs");
i->callback(Menu_view_page_bugs_cb);
- i = load_images_item = new Item("Load images");
- i->callback(Menu_load_images_cb);
+ stylesheets = new ItemGroup("View Stylesheets");
+ new Divider();
i = new Item("Bookmark this page");
i->callback(Menu_add_bookmark_cb);
- new Divider();
+ new Divider();
i = new Item("Find Text");
i->callback(Menu_find_text_cb);
//i->shortcut(CTRL+'f');
i = new Item("Jump to...");
i->deactivate();
- new Divider();
+ new Divider();
i = new Item("Save page As...");
i->callback(Menu_save_page_cb);
@@ -337,11 +376,49 @@ void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
else
view_page_bugs_item->deactivate();
- if (unloaded_imgs == TRUE) {
- load_images_item->activate();
- load_images_item->user_data(NULL); /* wildcard */
+ if (strncmp(URL_STR(url), "dpi:/vsource/", 13) == 0)
+ view_source_item->deactivate();
+ else
+ view_source_item->activate();
+
+ int n = stylesheets->children();
+ for (j = 0; j < n; j++) {
+ /* get rid of the old ones */
+ Widget *child = stylesheets->child(0);
+ dFree((char *)child->label());
+ a_Url_free((DilloUrl *)child->user_data());
+ delete child;
+ }
+
+ if (cssUrls && cssUrls->size () > 0) {
+ stylesheets->activate();
+ for (j = 0; j < cssUrls->size(); j++) {
+ /* may want ability to Load individual unloaded stylesheets as well */
+ const char *action = "View ";
+ DilloUrl *url = cssUrls->get(j);
+ const char *url_str = URL_STR(url);
+ const uint_t head_length = 30, tail_length = 40,
+ url_len = strlen(url_str);;
+ char *label;
+
+ if (url_len > head_length + tail_length + 3) {
+ /* trim long URLs when making the label */
+ char *url_head = dStrndup(url_str, head_length);
+ const char *url_tail = url_str + (url_len - tail_length);
+ label = dStrconcat(action, url_head, "...", url_tail, NULL);
+ dFree(url_head);
+ } else {
+ label = dStrconcat(action, url_str, NULL);
+ }
+
+ i = new Item(label);
+ i->set_flag(RAW_LABEL);
+ i->user_data(a_Url_dup(url));
+ i->callback(Menu_stylesheet_cb);
+ stylesheets->add(i);
+ }
} else {
- load_images_item->deactivate();
+ stylesheets->deactivate();
}
a_Timeout_add(0.0, Menu_popup_cb, (void *)pm);
@@ -367,12 +444,12 @@ void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url)
i->callback(Menu_open_url_nw_cb);
i = new Item("Open Link in New Tab");
i->callback(Menu_open_url_nt_cb);
- new Divider();
+ new Divider();
i = new Item("Bookmark this Link");
i->callback(Menu_add_bookmark_cb);
i = new Item("Copy Link location");
i->callback(Menu_copy_urlstr_cb);
- new Divider();
+ new Divider();
i = new Item("Save Link As...");
i->callback(Menu_save_link_cb);
@@ -387,21 +464,25 @@ void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url)
* Image popup menu (construction & popup)
*/
void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
- bool_t loaded_img, DilloUrl *link_url)
+ bool_t loaded_img, DilloUrl *page_url,
+ DilloUrl *link_url)
{
// One menu for every browser window
static PopupMenu *pm = 0;
// Active/inactive control.
static Item *link_menuitem = 0;
static Item *load_img_menuitem = 0;
+ static DilloUrl *popup_page_url = NULL;
static DilloUrl *popup_link_url = NULL;
popup_bw = bw;
a_Url_free(popup_url);
popup_url = a_Url_dup(url);
+ a_Url_free(popup_page_url);
+ popup_page_url = a_Url_dup(page_url);
a_Url_free(popup_link_url);
popup_link_url = a_Url_dup(link_url);
-
+
if (!pm) {
Item *i;
pm = new PopupMenu(0,0,0,0,"&IMAGE OPTIONS");
@@ -434,7 +515,7 @@ void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
load_img_menuitem->deactivate();
} else {
load_img_menuitem->activate();
- load_img_menuitem->user_data(popup_url);
+ load_img_menuitem->user_data(popup_page_url);
}
if (link_url) {
@@ -448,6 +529,39 @@ void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
}
/*
+ * Form popup menu (construction & popup)
+ */
+void a_Menu_form_popup(BrowserWindow *bw, const DilloUrl *page_url,
+ void *formptr, bool_t hidvis)
+{
+ static PopupMenu *pm = 0;
+ static Item *hiddens_item = 0;
+ static bool hiddens_visible;
+
+ popup_bw = bw;
+ a_Url_free(popup_url);
+ popup_url = a_Url_dup(page_url);
+ if (!pm) {
+ Item *i;
+ pm = new PopupMenu(0,0,0,0,"FORM OPTIONS");
+ pm->add(i = new Item("Submit form"));
+ i->callback(Menu_form_submit_cb);
+ pm->add(i = new Item("Reset form"));
+ i->callback(Menu_form_reset_cb);
+ pm->add(hiddens_item = new Item(""));
+ hiddens_item->callback(Menu_form_hiddens_cb);
+ hiddens_item->user_data(&hiddens_visible);
+ pm->type(PopupMenu::POPUP123);
+ }
+ pm->user_data(formptr);
+
+ hiddens_visible = hidvis;
+ hiddens_item->label(hiddens_visible ? "Hide hiddens": "Show hiddens");
+
+ a_Timeout_add(0.0, Menu_popup_cb, (void *)pm);
+}
+
+/*
* File popup menu (construction & popup)
*/
void a_Menu_file_popup(BrowserWindow *bw, void *v_wid)
@@ -466,21 +580,29 @@ void a_Menu_file_popup(BrowserWindow *bw, void *v_wid)
popup_url = NULL;
if (!pm) {
+ int shortcut;
Item *i;
pm = new PopupMenu(0,0,0,0,"File");
pm->begin();
- i = new Item("New Window", CTRL+'n', filemenu_cb, (void*)"nw");
- i = new Item("New Tab", CTRL+'t', filemenu_cb, (void*)"nt");
+ shortcut = Keys::getShortcut(KEYS_NEW_WINDOW);
+ i = new Item("New Window", shortcut, filemenu_cb, (void*)"nw");
+ shortcut = Keys::getShortcut(KEYS_NEW_TAB);
+ i = new Item("New Tab", shortcut, filemenu_cb, (void*)"nt");
new Divider();
- i = new Item("Open File...", CTRL+'o', filemenu_cb, (void*)"of");
- i = new Item("Open URL...", CTRL+'l', filemenu_cb, (void*)"ou");
- i = new Item("Close", CTRL+'q', filemenu_cb, (void*)"cw");
+ shortcut = Keys::getShortcut(KEYS_OPEN);
+ i = new Item("Open File...", shortcut, filemenu_cb, (void*)"of");
+ shortcut = Keys::getShortcut(KEYS_GOTO);
+ i = new Item("Open URL...", shortcut, filemenu_cb, (void*)"ou");
+ shortcut = Keys::getShortcut(KEYS_CLOSE_TAB);
+ i = new Item("Close", shortcut, filemenu_cb, (void*)"cw");
new Divider();
- i = new Item("Exit Dillo", ALT+'q', filemenu_cb, (void*)"ed");
+ shortcut = Keys::getShortcut(KEYS_CLOSE_ALL);
+ i = new Item("Exit Dillo", shortcut, filemenu_cb, (void*)"ed");
pm->type(PopupMenu::POPUP123);
pm->end();
}
+ pm->label(wid->visible() ? NULL : "File");
a_Timeout_add(0.0, Menu_popup_cb2, (void *)pm);
}
@@ -503,7 +625,7 @@ void a_Menu_bugmeter_popup(BrowserWindow *bw, const DilloUrl *url)
i->callback(Menu_bugmeter_validate_w3c_cb);
i = new Item("Validate URL with WDG");
i->callback(Menu_bugmeter_validate_wdg_cb);
- new Divider();
+ new Divider();
i = new Item("About Bug Meter...");
i->callback(Menu_bugmeter_about_cb);
pm->type(PopupMenu::POPUP123);
@@ -520,7 +642,6 @@ void a_Menu_bugmeter_popup(BrowserWindow *bw, const DilloUrl *url)
*/
void a_Menu_history_popup(BrowserWindow *bw, int direction)
{
- // One menu for every browser window
static PopupMenu *pm = 0;
Item *it;
int i;
@@ -547,7 +668,7 @@ void a_Menu_history_popup(BrowserWindow *bw, int direction)
for (i = 0; history_list[i] != -1; i += 1) {
// TODO: restrict title size
it = new CustItem(a_History_get_title(history_list[i], 1));
- it->callback(Menu_history_cb, (void*)(i+1));
+ it->callback(Menu_history_cb, INT2VOIDP(i+1));
}
pm->type(PopupMenu::POPUP123);
pm->end();
@@ -555,16 +676,70 @@ void a_Menu_history_popup(BrowserWindow *bw, int direction)
pm->popup();
}
+/*
+ * Toggle use of remote stylesheets
+ */
+static void Menu_remote_css_cb(Widget *wid)
+{
+ _MSG("Menu_remote_css_cb\n");
+ prefs.load_stylesheets = wid->state() ? 1 : 0;
+ a_UIcmd_repush(popup_bw);
+}
-void a_Menu_popup_set_url(BrowserWindow *bw, const DilloUrl *url) { }
-void a_Menu_popup_set_url2(BrowserWindow *bw, const DilloUrl *url) { }
-void a_Menu_popup_clear_url2(void *menu_popup) { }
+/*
+ * Toggle use of embedded CSS style
+ */
+static void Menu_embedded_css_cb(Widget *wid)
+{
+ prefs.parse_embedded_css = wid->state() ? 1 : 0;
+ a_UIcmd_repush(popup_bw);
+}
+
+/*
+ * Toggle loading of images -- and load them if enabling.
+ */
+static void Menu_imgload_toggle_cb(Widget *wid)
+{
+ if ((prefs.load_images = wid->state() ? 1 : 0)) {
+ void *doc = a_Bw_get_current_doc(popup_bw);
-DilloUrl *a_Menu_popup_get_url(BrowserWindow *bw) { return NULL; }
+ if (doc) {
+ DilloUrl *pattern = NULL;
+ a_Html_load_images(doc, pattern);
+ }
+ }
+}
-void a_Menu_pagemarks_new (BrowserWindow *bw) { }
-void a_Menu_pagemarks_destroy (BrowserWindow *bw) { }
-void a_Menu_pagemarks_add(BrowserWindow *bw, void *page, void *style,
- int level) { }
-void a_Menu_pagemarks_set_text(BrowserWindow *bw, const char *str) { }
+/*
+ * Tools popup menu (construction & popup)
+ */
+void a_Menu_tools_popup(BrowserWindow *bw, void *v_wid)
+{
+ // One menu shared by every browser window
+ static PopupMenu *pm = NULL;
+ Widget *wid = (Widget*)v_wid;
+ Item *it;
+
+ popup_bw = bw;
+
+ if (!pm) {
+ pm = new PopupMenu(0,0,0,0, "TOOLS");
+ pm->begin();
+ it = new ToggleItem("Use remote CSS");
+ it->callback(Menu_remote_css_cb);
+ it->state(prefs.load_stylesheets);
+ it = new ToggleItem("Use embedded CSS");
+ it->callback(Menu_embedded_css_cb);
+ it->state(prefs.parse_embedded_css);
+ new Divider();
+ it = new ToggleItem("Load images");
+ it->callback(Menu_imgload_toggle_cb);
+ it->state(prefs.load_images);
+ pm->type(PopupMenu::POPUP13);
+ pm->end();
+ }
+ //pm->popup();
+ pm->value(-1);
+ ((Menu*)pm)->popup(Rectangle(0,wid->h(),pm->w(),pm->h()));
+}
diff --git a/src/menu.hh b/src/menu.hh
index 9e8a73c6..668de001 100644
--- a/src/menu.hh
+++ b/src/menu.hh
@@ -8,26 +8,17 @@ extern "C" {
#endif /* __cplusplus */
void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
- bool_t has_bugs, bool_t unloaded_imgs);
+ bool_t has_bugs, void *v_cssUrls);
void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url);
void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
- bool_t loaded_img, DilloUrl *link_url);
+ bool_t loaded_img, DilloUrl *page_url,
+ DilloUrl *link_url);
+void a_Menu_form_popup(BrowserWindow *bw, const DilloUrl *page_url,
+ void *vform, bool_t showing_hiddens);
void a_Menu_file_popup(BrowserWindow *bw, void *v_wid);
void a_Menu_bugmeter_popup(BrowserWindow *bw, const DilloUrl *url);
void a_Menu_history_popup(BrowserWindow *bw, int direction);
-
-//---------------------
-void a_Menu_popup_set_url(BrowserWindow *bw, const DilloUrl *url);
-void a_Menu_popup_set_url2(BrowserWindow *bw, const DilloUrl *url);
-void a_Menu_popup_clear_url2(void *menu_popup);
-
-DilloUrl *a_Menu_popup_get_url(BrowserWindow *bw);
-
-void a_Menu_pagemarks_new (BrowserWindow *bw);
-void a_Menu_pagemarks_destroy (BrowserWindow *bw);
-void a_Menu_pagemarks_add(BrowserWindow *bw, void *page, void *style,
- int level);
-void a_Menu_pagemarks_set_text(BrowserWindow *bw, const char *str);
+void a_Menu_tools_popup(BrowserWindow *bw, void *v_wid);
#ifdef __cplusplus
diff --git a/src/misc.c b/src/misc.c
index 646a79dc..9ba8c9b9 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -12,21 +12,21 @@
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
#include <ctype.h>
+#include <assert.h>
+#include "utf8.hh"
#include "msg.h"
#include "misc.h"
-
/*
* Escape characters as %XX sequences.
* Return value: New string.
*/
char *a_Misc_escape_chars(const char *str, const char *esc_set)
{
- static const char *hex = "0123456789ABCDEF";
+ static const char *const hex = "0123456789ABCDEF";
char *p = NULL;
Dstr *dstr;
int i;
@@ -47,34 +47,54 @@ char *a_Misc_escape_chars(const char *str, const char *esc_set)
return p;
}
-
#define TAB_SIZE 8
/*
* Takes a string and converts any tabs to spaces.
*/
-char *a_Misc_expand_tabs(const char *str, int len)
+int
+a_Misc_expand_tabs(char **start, char *end, char *buf, int buflen)
{
- Dstr *New = dStr_new("");
- int i, j, pos, old_pos;
- char *val;
-
- if (len) {
- for (pos = 0, i = 0; i < len; i++) {
- if (str[i] == '\t') {
- /* Fill with whitespaces until the next tab. */
- old_pos = pos;
- pos += TAB_SIZE - (pos % TAB_SIZE);
- for (j = old_pos; j < pos; j++)
- dStr_append_c(New, ' ');
- } else {
- dStr_append_c(New, str[i]);
- pos++;
- }
+ int j, pos = 0, written = 0, old_pos, char_len;
+ uint_t code;
+ static const int combining_char_space = 32;
+
+ while (*start < end && written < buflen - TAB_SIZE - combining_char_space) {
+ code = a_Utf8_decode(*start, end, &char_len);
+
+ if (code == '\t') {
+ /* Fill with whitespaces until the next tab. */
+ old_pos = pos;
+ pos += TAB_SIZE - (pos % TAB_SIZE);
+ for (j = old_pos; j < pos; j++)
+ buf[written++] = ' ';
+ } else {
+ assert(char_len <= 4);
+ for (j = 0; j < char_len; j++)
+ buf[written++] = (*start)[j];
+ pos++;
}
+
+ *start += char_len;
}
- val = New->str;
- dStr_free(New, FALSE);
- return val;
+
+ /* If following chars are combining chars (e.g. accents) add them to the
+ * buffer. We have reserved combining_char_space bytes for this.
+ * If there should be more combining chars, we split nevertheless.
+ */
+ while (*start < end && written < buflen - 4) {
+ code = a_Utf8_decode(*start, end, &char_len);
+
+ if (! a_Utf8_combining_char(code))
+ break;
+
+ assert(char_len <= 4);
+ for (j = 0; j < char_len; j++)
+ buf[written++] = (*start)[j];
+
+ *start += char_len;
+ }
+
+ return written;
}
/* TODO: could use dStr ADT! */
@@ -120,7 +140,7 @@ int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
DetectedContentType Type = DT_OCTET_STREAM; /* default to binary */
/* HTML try */
- for (i = 0; i < Size && isspace(p[i]); ++i);
+ for (i = 0; i < Size && dIsspace(p[i]); ++i);
if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) ||
(Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) ||
(Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) ||
@@ -150,7 +170,7 @@ int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
* All in the above set regard [00-31] as control characters.
* LATIN1: [7F-9F] unused
* CP-1251 {7F,98} unused (two characters).
- *
+ *
* We'll use [0-31] as indicators of non-text content.
* Better heuristics are welcomed! :-) */
@@ -158,19 +178,23 @@ int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
Size = MIN (Size, 256);
for (i = 0; i < Size; i++) {
int ch = (uchar_t) p[i];
- if (ch < 32 && !isspace(ch))
+ if (ch < 32 && !dIsspace(ch))
++bin_chars;
if (ch > 126)
++non_ascci;
if (ch > 190)
++non_ascci_text;
}
- if (bin_chars == 0) {
+ if (bin_chars == 0 && (non_ascci - non_ascci_text) <= Size/10) {
/* Let's say text: if "rare" chars are <= 10% */
- if ((non_ascci - non_ascci_text) <= Size/10)
+ Type = DT_TEXT_PLAIN;
+ } else if (Size > 0) {
+ /* a special check for UTF-8 */
+ Size = a_Utf8_end_of_char(p, Size - 1) + 1;
+ if (a_Utf8_test(p, Size) > 0)
Type = DT_TEXT_PLAIN;
}
- if (Size == 256)
+ if (Size >= 256)
st = 0;
}
@@ -180,11 +204,13 @@ int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
/*
* Parse Content-Type string, e.g., "text/html; charset=utf-8".
+ * Content-Type is defined in RFC 2045 section 5.1.
*/
-void a_Misc_parse_content_type(const char *str, char **major, char **minor,
+void a_Misc_parse_content_type(const char *type, char **major, char **minor,
char **charset)
{
- const char *s;
+ static const char tspecials_space[] = "()<>@,;:\\\"/[]?= ";
+ const char *str, *s;
if (major)
*major = NULL;
@@ -192,20 +218,31 @@ void a_Misc_parse_content_type(const char *str, char **major, char **minor,
*minor = NULL;
if (charset)
*charset = NULL;
- if (!str)
+ if (!(str = type))
return;
- for (s = str; isalnum(*s) || (*s == '-'); s++);
+ for (s = str; *s && !iscntrl((uchar_t)*s) && !strchr(tspecials_space, *s);
+ s++) ;
if (major)
*major = dStrndup(str, s - str);
if (*s == '/') {
- for (str = ++s; isalnum(*s) || (*s == '-'); s++);
+ for (str = ++s;
+ *s && !iscntrl((uchar_t)*s) && !strchr(tspecials_space, *s); s++) ;
if (minor)
*minor = dStrndup(str, s - str);
}
-
- if (charset && *s) {
+ if (charset && *s &&
+ (dStrncasecmp(type, "text/", 5) == 0 ||
+ dStrncasecmp(type, "application/xhtml+xml", 21) == 0)) {
+ /* "charset" parameter defined for text media type in RFC 2046,
+ * application/xhtml+xml in RFC 3236.
+ *
+ * Note that RFC 3023 lists some main xml media types and provides
+ * the convention of using the "+xml" minor type suffix for other
+ * xml types, so it would be reasonable to check for that suffix if
+ * we have need to care about various xml types someday.
+ */
const char terminators[] = " ;\t";
const char key[] = "charset";
@@ -346,9 +383,9 @@ int a_Misc_parse_geometry(char *str, int *x, int *y, int *w, int *h)
*/
char *a_Misc_encode_base64(const char *in)
{
- static const char *base64_hex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789+/";
+ static const char *const base64_hex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
char *out = NULL;
int len, i = 0;
diff --git a/src/misc.h b/src/misc.h
index d8de0238..0b4eaaa5 100644
--- a/src/misc.h
+++ b/src/misc.h
@@ -10,7 +10,7 @@ extern "C" {
char *a_Misc_escape_chars(const char *str, const char *esc_set);
-char *a_Misc_expand_tabs(const char *str, int len);
+int a_Misc_expand_tabs(char **start, char *end, char *buf, int buflen);
int a_Misc_get_content_type_from_data(void *Data, size_t Size,const char **PT);
int a_Misc_content_type_check(const char *EntryType, const char *DetectedType);
void a_Misc_parse_content_type(const char *str, char **major, char **minor,
diff --git a/src/msg.h b/src/msg.h
index 245ee803..c6ddcb48 100644
--- a/src/msg.h
+++ b/src/msg.h
@@ -11,28 +11,17 @@
#define _MSG_WARN(...)
#define _MSG_HTTP(...)
-
-#define MSG(...) \
+#define MSG_INNARDS(prefix, ...) \
D_STMT_START { \
if (prefs.show_msg){ \
- printf(__VA_ARGS__); \
+ printf(prefix __VA_ARGS__); \
fflush (stdout); \
} \
} D_STMT_END
-#define MSG_WARN(...) \
- D_STMT_START { \
- if (prefs.show_msg) \
- printf("** WARNING **: " __VA_ARGS__); \
- } D_STMT_END
-
-#define MSG_ERR(...) \
- D_STMT_START { \
- if (prefs.show_msg) \
- printf("** ERROR **: " __VA_ARGS__); \
- } D_STMT_END
-
-#define MSG_HTTP(...) \
- printf("HTTP warning: " __VA_ARGS__)
+#define MSG(...) MSG_INNARDS("", __VA_ARGS__)
+#define MSG_WARN(...) MSG_INNARDS("** WARNING **: ", __VA_ARGS__)
+#define MSG_ERR(...) MSG_INNARDS("** ERROR **: ", __VA_ARGS__)
+#define MSG_HTTP(...) MSG_INNARDS("HTTP warning: ", __VA_ARGS__)
#endif /* __MSG_H__ */
diff --git a/src/nav.c b/src/nav.c
index 2433d7b2..740bc565 100644
--- a/src/nav.c
+++ b/src/nav.c
@@ -163,7 +163,7 @@ static void Nav_save_scroll_pos(BrowserWindow *bw, int idx, int posx, int posy)
}
/*
- * Remove equal adyacent URLs at the top of the stack.
+ * Remove equal adjacent URLs at the top of the stack.
* (It may happen with redirections)
*/
static void Nav_stack_clean(BrowserWindow *bw)
@@ -191,25 +191,26 @@ static void Nav_stack_clean(BrowserWindow *bw)
* This function requests the page's root-URL; images and related stuff
* are fetched directly by the HTML module.
*/
-static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url, int offset)
+static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url,
+ const DilloUrl *requester, int offset)
{
- DilloUrl *old_url;
- bool_t MustLoad, ForceReload;
+ const DilloUrl *old_url;
+ bool_t MustLoad, ForceReload, Repush, IgnoreScroll;
int x, y, idx, ClientKey;
DilloWeb *Web;
MSG("Nav_open_url: new url='%s'\n", URL_STR_(url));
+ Repush = (URL_FLAGS(url) & URL_ReloadFromCache) != 0;
ForceReload = (URL_FLAGS(url) & (URL_E2EQuery + URL_ReloadFromCache)) != 0;
+ IgnoreScroll = (URL_FLAGS(url) & URL_IgnoreScroll) != 0;
/* Get the url of the current page */
idx = a_Nav_stack_ptr(bw);
old_url = a_History_get_url(NAV_UIDX(bw, idx));
_MSG("Nav_open_url: old_url='%s' idx=%d\n", URL_STR(old_url), idx);
/* Record current scrolling position */
- if (URL_FLAGS(url) & URL_ReloadFromCache) {
- /* Repush operation, don't change scroll position */
- } else if (old_url) {
+ if (old_url && !IgnoreScroll) {
a_UIcmd_get_scroll_xy(bw, &x, &y);
Nav_save_scroll_pos(bw, idx, x, y);
_MSG("Nav_open_url: saved scroll of '%s' at x=%d y=%d\n",
@@ -232,7 +233,7 @@ static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url, int offset)
// a_Menu_pagemarks_new(bw);
- Web = a_Web_new(url);
+ Web = a_Web_new(url, requester);
Web->bw = bw;
Web->flags |= WEB_RootUrl;
if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
@@ -249,12 +250,21 @@ static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url, int offset)
void a_Nav_cancel_expect(BrowserWindow *bw)
{
if (bw->nav_expecting) {
- if (bw->nav_expect_url) {
- a_Url_free(bw->nav_expect_url);
- bw->nav_expect_url = NULL;
- }
+ a_Url_free(bw->nav_expect_url);
+ bw->nav_expect_url = NULL;
bw->nav_expecting = FALSE;
}
+ if (bw->meta_refresh_status > 0)
+ --bw->meta_refresh_status;
+}
+
+/*
+ * Cancel the expect if 'url' matches.
+ */
+void a_Nav_cancel_expect_if_eq(BrowserWindow *bw, const DilloUrl *url)
+{
+ if (bw->nav_expecting && a_Url_cmp(url, bw->nav_expect_url) == 0)
+ a_Nav_cancel_expect(bw);
}
/*
@@ -265,7 +275,7 @@ void a_Nav_cancel_expect(BrowserWindow *bw)
*/
void a_Nav_expect_done(BrowserWindow *bw)
{
- int url_idx, posx, posy, reload, repush, e2equery, goto_old_scroll = TRUE;
+ int m, url_idx, posx, posy, reload, repush, e2equery, goto_old_scroll=TRUE;
DilloUrl *url;
char *fragment = NULL;
@@ -278,11 +288,10 @@ void a_Nav_expect_done(BrowserWindow *bw)
e2equery = (URL_FLAGS(url) & URL_E2EQuery);
fragment = a_Url_decode_hex_str(URL_FRAGMENT_(url));
- /* Unset E2EQuery, ReloadPage and ReloadFromCache
+ /* Unset E2EQuery, ReloadPage, ReloadFromCache and IgnoreScroll
* before adding this url to history */
- a_Url_set_flags(url, URL_FLAGS(url) & ~URL_E2EQuery);
- a_Url_set_flags(url, URL_FLAGS(url) & ~URL_ReloadPage);
- a_Url_set_flags(url, URL_FLAGS(url) & ~URL_ReloadFromCache);
+ m = URL_E2EQuery|URL_ReloadPage|URL_ReloadFromCache|URL_IgnoreScroll;
+ a_Url_set_flags(url, URL_FLAGS(url) & ~m);
url_idx = a_History_add_url(url);
if (repush) {
@@ -310,7 +319,7 @@ void a_Nav_expect_done(BrowserWindow *bw)
}
if (goto_old_scroll) {
- /* Scroll to were we were in this page */
+ /* Scroll to where we were in this page */
Nav_get_scroll_pos(bw, &posx, &posy);
a_UIcmd_set_scroll_xy(bw, posx, posy);
_MSG("Nav: expect_done scrolling to x=%d y=%d\n", posx, posy);
@@ -333,7 +342,8 @@ void a_Nav_expect_done(BrowserWindow *bw)
* - Set bw to expect the URL data
* - Ask the cache to feed back the requested URL (via Nav_open_url)
*/
-void a_Nav_push(BrowserWindow *bw, const DilloUrl *url)
+void a_Nav_push(BrowserWindow *bw, const DilloUrl *url,
+ const DilloUrl *requester)
{
dReturn_if_fail (bw != NULL);
@@ -345,7 +355,7 @@ void a_Nav_push(BrowserWindow *bw, const DilloUrl *url)
a_Nav_cancel_expect(bw);
bw->nav_expect_url = a_Url_dup(url);
bw->nav_expecting = TRUE;
- Nav_open_url(bw, url, 0);
+ Nav_open_url(bw, url, requester, 0);
}
/*
@@ -362,13 +372,14 @@ static void Nav_repush(BrowserWindow *bw)
a_Url_set_flags(url, URL_FLAGS(url) | URL_ReloadFromCache);
bw->nav_expect_url = a_Url_dup(url);
bw->nav_expecting = TRUE;
- Nav_open_url(bw, url, 0);
+ Nav_open_url(bw, url, NULL, 0);
a_Url_free(url);
}
}
static void Nav_repush_callback(void *data)
{
+ _MSG(">>>> Nav_repush_callback <<<<\n");
Nav_repush(data);
a_Timeout_remove();
}
@@ -377,32 +388,50 @@ static void Nav_repush_callback(void *data)
* Repush current URL: not an end-to-end reload but from cache.
* - Currently used to switch to a charset decoder given by the META element.
* - Delayed to let dillo finish the call flow into a known state.
+ *
+ * There's no need to stop the parser before calling this function:
+ * When the timeout activates, a_Bw_stop_clients will stop the data feed.
*/
void a_Nav_repush(BrowserWindow *bw)
{
dReturn_if_fail (bw != NULL);
+ MSG(">>> a_Nav_repush <<<<\n");
a_Timeout_add(0.0, Nav_repush_callback, (void*)bw);
}
/*
- * Same as a_Nav_push() but in a new window.
+ * This one does a_Nav_redirection0's job.
*/
-void a_Nav_push_nw(BrowserWindow *bw, const DilloUrl *url)
+static void Nav_redirection0_callback(void *data)
{
- int w, h;
- BrowserWindow *newbw;
+ BrowserWindow *bw = (BrowserWindow *)data;
+ const DilloUrl *referer_url = a_History_get_url(NAV_TOP_UIDX(bw));
+ _MSG(">>>> Nav_redirection0_callback <<<<\n");
- a_UIcmd_get_wh(bw, &w, &h);
- newbw = a_UIcmd_browser_window_new(w, h, bw);
- a_Nav_push(newbw, url);
+ if (bw->meta_refresh_status == 2) {
+ Nav_stack_move_ptr(bw, -1);
+ a_Nav_push(bw, bw->meta_refresh_url, referer_url);
+ }
+ a_Url_free(bw->meta_refresh_url);
+ bw->meta_refresh_url = NULL;
+ bw->meta_refresh_status = 0;
+ a_Timeout_remove();
}
/*
- * Wraps a_Nav_push to match 'DwPage->link' function type
+ * Handle a zero-delay URL redirection given by META
*/
-void a_Nav_vpush(void *vbw, const DilloUrl *url)
+void a_Nav_redirection0(BrowserWindow *bw, const DilloUrl *new_url)
{
- a_Nav_push(vbw, url);
+ dReturn_if_fail (bw != NULL);
+ _MSG(">>> a_Nav_redirection0 <<<<\n");
+
+ a_Url_free(bw->meta_refresh_url);
+ bw->meta_refresh_url = a_Url_dup(new_url);
+ a_Url_set_flags(bw->meta_refresh_url,
+ URL_FLAGS(new_url)|URL_E2EQuery|URL_IgnoreScroll);
+ bw->meta_refresh_status = 2;
+ a_Timeout_add(0.0, Nav_redirection0_callback, (void*)bw);
}
/*
@@ -415,7 +444,7 @@ void a_Nav_back(BrowserWindow *bw)
a_Nav_cancel_expect(bw);
if (--idx >= 0){
a_UIcmd_set_msg(bw, "");
- Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), -1);
+ Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), NULL, -1);
}
}
@@ -429,7 +458,7 @@ void a_Nav_forw(BrowserWindow *bw)
a_Nav_cancel_expect(bw);
if (++idx < a_Nav_stack_size(bw)) {
a_UIcmd_set_msg(bw, "");
- Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), +1);
+ Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), NULL, +1);
}
}
@@ -438,56 +467,56 @@ void a_Nav_forw(BrowserWindow *bw)
*/
void a_Nav_home(BrowserWindow *bw)
{
- a_Nav_push(bw, prefs.home);
+ a_Nav_push(bw, prefs.home, NULL);
}
/*
* This one does a_Nav_reload's job!
*/
-static void Nav_reload(BrowserWindow *bw)
+static void Nav_reload_callback(void *data)
{
- DilloUrl *ReqURL;
+ BrowserWindow *bw = data;
+ const DilloUrl *h_url;
+ DilloUrl *r_url;
+ int choice, confirmed = 1;
a_Nav_cancel_expect(bw);
if (a_Nav_stack_size(bw)) {
- ReqURL = a_Url_dup(a_History_get_url(NAV_TOP_UIDX(bw)));
- /* Mark URL as reload to differentiate from push */
- a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) | URL_ReloadPage);
- /* Let's make reload be end-to-end */
- a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) | URL_E2EQuery);
- /* This is an explicit reload, so clear the SpamSafe flag */
- a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) & ~URL_SpamSafe);
- bw->nav_expect_url = ReqURL;
- bw->nav_expecting = TRUE;
- Nav_open_url(bw, ReqURL, 0);
+ h_url = a_History_get_url(NAV_TOP_UIDX(bw));
+ if (strncmp(URL_STR(h_url), "dpi:/vsource/", 13) == 0) {
+ /* disable reload for view source dpi */
+ confirmed = 0;
+ } else if (URL_FLAGS(h_url) & URL_Post) {
+ /* Attempt to repost data, let's confirm... */
+ choice = a_Dialog_choice3("Repost form data?",
+ "Yes", "*No", "Cancel");
+ confirmed = (choice == 0); /* "Yes" */
+ }
+
+ if (confirmed) {
+ r_url = a_Url_dup(h_url);
+ /* Mark URL as reload to differentiate from push */
+ a_Url_set_flags(r_url, URL_FLAGS(r_url) | URL_ReloadPage);
+ /* Let's make reload be end-to-end */
+ a_Url_set_flags(r_url, URL_FLAGS(r_url) | URL_E2EQuery);
+ /* This is an explicit reload, so clear the SpamSafe flag */
+ a_Url_set_flags(r_url, URL_FLAGS(r_url) & ~URL_SpamSafe);
+ bw->nav_expect_url = r_url;
+ bw->nav_expecting = TRUE;
+ Nav_open_url(bw, r_url, NULL, 0);
+ }
}
}
/*
* Implement the RELOAD button functionality.
* (Currently it only reloads the page, not its images)
+ * Note: the timeout lets CCC operations end before making the request.
*/
void a_Nav_reload(BrowserWindow *bw)
{
- DilloUrl *url;
- int choice;
-
- a_Nav_cancel_expect(bw);
- if (a_Nav_stack_size(bw)) {
- url = a_History_get_url(NAV_TOP_UIDX(bw));
- if (URL_FLAGS(url) & URL_Post) {
- /* Attempt to repost data, let's confirm... */
- choice = a_Dialog_choice3("Repost form data?",
- "Yes", "*No", "Cancel");
- if (choice == 0) { /* "Yes" */
- _MSG("Nav_reload_confirmed\n");
- Nav_reload(bw);
- }
-
- } else {
- Nav_reload(bw);
- }
- }
+ dReturn_if_fail (bw != NULL);
+ a_Timeout_add(0.0, Nav_reload_callback, (void*)bw);
}
/*
@@ -498,10 +527,10 @@ void a_Nav_jump(BrowserWindow *bw, int offset, int new_bw)
int idx = a_Nav_stack_ptr(bw) + offset;
if (new_bw) {
- a_Nav_push_nw(bw, a_History_get_url(NAV_UIDX(bw,idx)));
+ a_UIcmd_open_url_nw(bw, a_History_get_url(NAV_UIDX(bw,idx)));
} else {
a_Nav_cancel_expect(bw);
- Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), offset);
+ Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), NULL, offset);
a_UIcmd_set_buttons_sens(bw);
}
}
@@ -538,9 +567,9 @@ static void Nav_save_cb(int Op, CacheClient_t *Client)
void a_Nav_save_url(BrowserWindow *bw,
const DilloUrl *url, const char *filename)
{
- DilloWeb *Web = a_Web_new(url);
+ DilloWeb *Web = a_Web_new(url, NULL);
Web->bw = bw;
- Web->filename = dStrdup(filename);
+ Web->filename = dStrdup(filename);
Web->flags |= WEB_Download;
/* TODO: keep track of this client */
a_Capi_open_url(Web, Nav_save_cb, Web);
@@ -559,5 +588,13 @@ int a_Nav_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
*/
void a_Nav_unref_buf(const DilloUrl *Url)
{
- return a_Capi_unref_buf(Url);
+ a_Capi_unref_buf(Url);
+}
+
+/*
+ * Wrapper for a_Capi_set_vsource_url().
+ */
+void a_Nav_set_vsource_url(const DilloUrl *Url)
+{
+ a_Capi_set_vsource_url(Url);
}
diff --git a/src/nav.h b/src/nav.h
index 4a03960a..ad1270d7 100644
--- a/src/nav.h
+++ b/src/nav.h
@@ -13,9 +13,9 @@
extern "C" {
#endif /* __cplusplus */
-void a_Nav_push(BrowserWindow *bw, const DilloUrl *url);
-void a_Nav_push_nw(BrowserWindow *bw, const DilloUrl *url);
-void a_Nav_vpush(void *vbw, const DilloUrl *url);
+void a_Nav_redirection0(BrowserWindow *bw, const DilloUrl *new_url);
+void a_Nav_push(BrowserWindow *bw, const DilloUrl *url,
+ const DilloUrl *requester);
void a_Nav_repush(BrowserWindow *bw);
void a_Nav_back(BrowserWindow *bw);
void a_Nav_forw(BrowserWindow *bw);
@@ -24,6 +24,7 @@ void a_Nav_reload(BrowserWindow *bw);
void a_Nav_jump(BrowserWindow *bw, int offset, int new_bw);
void a_Nav_free(BrowserWindow *bw);
void a_Nav_cancel_expect (BrowserWindow *bw);
+void a_Nav_cancel_expect_if_eq(BrowserWindow *bw, const DilloUrl *url);
void a_Nav_expect_done(BrowserWindow *bw);
int a_Nav_stack_ptr(BrowserWindow *bw);
int a_Nav_stack_size(BrowserWindow *bw);
@@ -34,6 +35,7 @@ void a_Nav_save_url(BrowserWindow *bw,
const DilloUrl *url, const char *filename);
int a_Nav_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
void a_Nav_unref_buf(const DilloUrl *Url);
+void a_Nav_set_vsource_url(const DilloUrl *Url);
#ifdef __cplusplus
}
diff --git a/src/paths.cc b/src/paths.cc
new file mode 100644
index 00000000..6fccb89a
--- /dev/null
+++ b/src/paths.cc
@@ -0,0 +1,101 @@
+/*
+ * File: paths.cc
+ *
+ * Copyright 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "msg.h"
+#include "../dlib/dlib.h"
+#include "paths.hh"
+
+/*
+ * Local data
+ */
+
+// Dillo works from an unmounted directory (/tmp)
+static char* oldWorkingDir = NULL;
+
+/*
+ * Changes current working directory to /tmp and creates ~/.dillo
+ * if not exists.
+ */
+void Paths::init(void)
+{
+ char *path;
+ struct stat st;
+ int rc = 0;
+
+ dFree(oldWorkingDir);
+ oldWorkingDir = dGetcwd();
+ rc = chdir("/tmp");
+ if (rc == -1) {
+ MSG("paths: error changing directory to /tmp: %s\n",
+ dStrerror(errno));
+ }
+
+ path = dStrconcat(dGethomedir(), "/.dillo", NULL);
+ if (stat(path, &st) == -1) {
+ if (errno == ENOENT) {
+ MSG("paths: creating directory %s.\n", path);
+ if (mkdir(path, 0700) < 0) {
+ MSG("paths: error creating directory %s: %s\n",
+ path, dStrerror(errno));
+ }
+ } else {
+ MSG("Dillo: error reading %s: %s\n", path, dStrerror(errno));
+ }
+ }
+
+ dFree(path);
+}
+
+/*
+ * Return the initial current working directory in a string.
+ */
+char *Paths::getOldWorkingDir(void)
+{
+ return oldWorkingDir;
+}
+
+/*
+ * Free memory
+ */
+void Paths::free(void)
+{
+ dFree(oldWorkingDir);
+}
+
+/*
+ * Examines the path for "rcFile" and assign its file pointer to "fp".
+ */
+FILE *Paths::getPrefsFP(const char *rcFile)
+{
+ FILE *fp;
+ char *path = dStrconcat(dGethomedir(), "/.dillo/", rcFile, NULL);
+
+ if (!(fp = fopen(path, "r"))) {
+ MSG("paths: Cannot open file '%s'\n", path);
+
+ char *path2 = dStrconcat(DILLO_SYSCONF, rcFile, NULL);
+ if (!(fp = fopen(path2, "r"))) {
+ MSG("paths: Cannot open file '%s'\n",path2);
+ MSG("paths: Using internal defaults...\n");
+ } else {
+ MSG("paths: Using %s\n", path2);
+ }
+ dFree(path2);
+ }
+
+ dFree(path);
+ return fp;
+}
+
diff --git a/src/paths.hh b/src/paths.hh
new file mode 100644
index 00000000..a9efc62b
--- /dev/null
+++ b/src/paths.hh
@@ -0,0 +1,26 @@
+/*
+ * File: paths.hh
+ *
+ * Copyright 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PATHS_HH__
+#define __PATHS_HH__
+
+#define PATHS_RC_PREFS "dillorc"
+#define PATHS_RC_KEYS "keysrc"
+
+class Paths {
+public:
+ static void init(void);
+ static void free(void);
+ static char *getOldWorkingDir(void);
+ static FILE *getPrefsFP(const char *rcFile);
+};
+
+#endif /* __PATHS_HH__ */
diff --git a/src/pixmaps.h b/src/pixmaps.h
index 4e54ff71..83235dd2 100644
--- a/src/pixmaps.h
+++ b/src/pixmaps.h
@@ -6,7 +6,7 @@
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*/
@@ -675,6 +675,7 @@ static const char *const stop_xpm[] = {
" .KLLLLLLLK. ",
" ......... ",
" "};
+
/* XPM */
static const char *const bm_xpm[] = {
"22 22 86 1",
@@ -787,6 +788,57 @@ static const char *const bm_xpm[] = {
" .S. TU. ",
" . . "};
+/* XPM */
+static const char *const tools_xpm[] = {
+"22 22 25 1",
+" c #None",
+". c #696977777676",
+"X c #96969B9B9B9B",
+"o c #848490908F8F",
+"O c #D7D7D7D7D7D7",
+"+ c #B5B5B7B7B7B7",
+"@ c #EAEAEAEAEAEA",
+"# c #797987878787",
+"$ c #F1F1EFEFEFEF",
+"% c #CCCCCCCCCCCC",
+"& c #8B8B97979696",
+"* c #CACACACACACA",
+"= c #787878787878",
+"- c #AAAAB5B5B5B5",
+"; c #F9F9F8F8F9F9",
+": c #A6A6A8A8A8A8",
+"> c #7F7F90907F7F",
+", c #A4A4BDBDA4A4",
+"< c #A5A58A8A5353",
+"1 c #00008B8B0000",
+"2 c #6C6C6C6C6C6C",
+"3 c #BCBCC3C3C2C2",
+"4 c #CACAD8D8D8D8",
+"5 c #9999A8A8A8A8",
+"6 c #848489898989",
+" ",
+" ",
+" .XXo. ",
+" oO $$+. ",
+" oo@$$$@# ",
+" #o@$$$O# ",
+" #X$$$$%X ",
+" &#% #X$$$@o ",
+" #*o =-@$$$# ",
+" oO*o #$@$$$$# ",
+" oOOOX oO@;$$$$X ",
+" #@O*OXo:>,<$$$$o ",
+" XO@O*O*12>,<$$$# ",
+" X$@O*+<12>,<$$& ",
+" +$@O*+<12>,<;@ ",
+" :$@O*+<12>,<$@ ",
+" &34O*-<11>,<$$ ",
+" -X&X&<12>,<$O ",
+" 45<11>,<3 ",
+" 45<1=>O# ",
+" -&X6&=# ",
+" "};
+
/* Small icons here */
/* XPM */
@@ -1321,6 +1373,73 @@ static const char *const bm_s_xpm[] = {
" .v. wx. "};
/* XPM */
+static const char *const tools_s_xpm[] = {
+"16 16 45 1",
+" c #7B127D1C7D08",
+". c #82778EED8E15",
+"X c #8F3F975396FF",
+"o c #9A98A40CA38F",
+"O c #833D8EC28E02",
+"+ c #99C89EDF9EC2",
+"@ c #B347BD91BD42",
+"# c #7E4181A081AF",
+"$ c #8744915990DA",
+"% c #86728A968A7C",
+"& c #8D56924B9226",
+"* c #96DF9AC79AA6",
+"= c #9E01A473A438",
+"- c #AB41B55EB546",
+"; c #ABD1B155B117",
+": c #B5BFBC3DBC2F",
+"> c #B917BCECBCAB",
+", c #B558B98BB958",
+"< c #CEA2CFDBCFD4",
+"1 c #A699AC55ABF1",
+"2 c #C2A5C5A8C57A",
+"3 c #C8B2CA27CA1C",
+"4 c #D80FD952D955",
+"5 c #DF19E120E134",
+"6 c #EAC7E9ECE9FE",
+"7 c #F164EF95EF9D",
+"8 c #F6E0F5E6F61C",
+"9 c #F013EEAEEEBF",
+"0 c #487651675130",
+"q c #6E4D7BA97B06",
+"w c #88F190679051",
+"e c #92339C0E9BC1",
+"r c #A1BBAB0FAA98",
+"t c #A24AA7C4A7A7",
+"y c #86C7935E92DC",
+"u c #C8C4D721D725",
+"i c #D0E6D516D518",
+"p c #76A38450834A",
+"a c #6B4B7B507A6D",
+"s c #7C6D882B878D",
+"d c #88F395099487",
+"f c #DB34DCE9DCF5",
+"g c #5B05673F66CD",
+"h c #9402A0549FCC",
+" c None",
+/* pixels */
+" ",
+" gO+Oq ",
+" p*45<Xg ",
+" h.>885oa ",
+" p.3883Xg ",
+" i$O s$488;s ",
+" O>1p e;588;a ",
+" O331sr;7778:s ",
+" O343,**3678;a ",
+" f,643;#%368:s ",
+" t<64>= &374f ",
+" t244>= &379ih",
+" d@:;=& &475u",
+" ssr-* *44r",
+" u-%#*%0",
+" feww0g"
+};
+
+/* XPM */
static const char *const new_s_xpm[] = {
"11 11 35 1",
" c None",
@@ -1402,6 +1521,42 @@ static const char *const search_xpm[] = {
" 11 "};
/* XPM */
+static const char *const help_xpm[] = {
+"14 16 16 1",
+" c None",
+"1 c #DBDBDB",
+"2 c #B6B6B6",
+"3 c #929292",
+"4 c #6D6D6D",
+"5 c #F1EFEF",
+"6 c #018B00",
+"7 c #A48A53",
+"8 c #A4BDA4",
+"9 c #000000",
+"A c #000000",
+"B c #000000",
+"C c #000000",
+"D c #000000",
+"E c #000000",
+"F c #000000",
+" 5555555 ",
+" 544433355 ",
+" 54555438455 ",
+" 44555542835 ",
+" 44445548245 ",
+" 4444554274 ",
+" 544554274 ",
+" 55474 ",
+" 5474 ",
+" 544 ",
+" 44 ",
+" ",
+" 5445 ",
+" 4764 ",
+" 4674 ",
+" 5445 "};
+
+/* XPM */
static const char *const full_screen_on_xpm[] = {
"13 15 2 1",
" c None",
@@ -1496,48 +1651,6 @@ static const char *const mini_ok_xpm[] = {
};
/* XPM */
-static const char *const imgload_on_xpm[] = {
-"15 15 2 1",
-" c None",
-". c #00000000CF3C",
-" ",
-" . . . ... ",
-" . .. .. . . ",
-" . . . . . ",
-" . . . . .. ",
-" . . . . . ",
-" . . . ... ",
-" ",
-" ... . . ",
-" . . .. . ",
-" . . . . . ",
-" . . . .. ",
-" . . . . ",
-" ... . . ",
-" "};
-
-/* XPM */
-static const char *const imgload_off_xpm[] = {
-"15 15 2 1",
-" c None",
-". c #CF3C00000000",
-" ",
-" . . . ... ",
-" . .. .. . . ",
-" . . . . . ",
-" . . . . .. ",
-" . . . . . ",
-" . . . ... ",
-" ",
-" ... ... ... ",
-" . . . . ",
-" . . ... ... ",
-" . . . . ",
-" . . . . ",
-" ... . . ",
-" "};
-
-/* XPM */
static const char *const left_i_xpm[] = {
"22 22 3 1",
" c None",
diff --git a/src/plain.cc b/src/plain.cc
index b75dbea5..4da618e4 100644
--- a/src/plain.cc
+++ b/src/plain.cc
@@ -13,15 +13,13 @@
* Module for decoding a text/plain object into a dw widget.
*/
-#include <string.h> /* for memcpy and memmove */
-#include <math.h> /* for rint() */
-
#include "msg.h"
#include "prefs.h"
#include "cache.h"
#include "bw.h"
#include "web.hh"
#include "misc.h"
+#include "styleengine.hh"
#include "uicmd.hh"
@@ -37,23 +35,25 @@ using namespace dw::core;
class DilloPlain {
private:
- class PlainEventReceiver: public dw::core::Widget::EventReceiver {
+ class PlainLinkReceiver: public dw::core::Layout::LinkReceiver {
public:
DilloPlain *plain;
- bool buttonPress(dw::core::Widget *widget, dw::core::EventButton *event);
+ bool press(dw::core::Widget *widget, int link, int img, int x, int y,
+ dw::core::EventButton *event);
};
- PlainEventReceiver plainReceiver;
+ PlainLinkReceiver plainReceiver;
+
+ void addLine(char *Buf, uint_t BufSize);
public:
BrowserWindow *bw;
- DilloUrl *url;
Widget *dw;
style::Style *widgetStyle;
size_t Start_Ofs; /* Offset of where to start reading next */
int state;
- DilloPlain(BrowserWindow *bw, const DilloUrl *url);
+ DilloPlain(BrowserWindow *bw);
~DilloPlain();
void write(void *Buf, uint_t BufSize, int Eof);
@@ -83,38 +83,27 @@ void a_Plain_free(void *data);
/*
* Diplain constructor.
*/
-DilloPlain::DilloPlain(BrowserWindow *p_bw, const DilloUrl *p_url)
+DilloPlain::DilloPlain(BrowserWindow *p_bw)
{
- style::StyleAttrs styleAttrs;
- style::FontAttrs fontAttrs;
-
/* Init event receiver */
plainReceiver.plain = this;
/* Init internal variables */
bw = p_bw;
- url = a_Url_dup(p_url);
dw = new Textblock (prefs.limit_text_width);
Start_Ofs = 0;
state = ST_SeekingEol;
- /* Create the font and attribute for the page. */
- fontAttrs.name = prefs.fw_fontname;
- fontAttrs.size = (int) rint(14.0 * prefs.font_factor);
- fontAttrs.weight = 400;
- fontAttrs.style = style::FONT_STYLE_NORMAL;
-
- Layout *layout = (Layout*)bw->render_layout;
- styleAttrs.initValues ();
- styleAttrs.margin.setVal (5);
- styleAttrs.font = style::Font::create (layout, &fontAttrs);
- styleAttrs.color = style::Color::createSimple (layout, prefs.text_color);
- styleAttrs.backgroundColor =
- style::Color::createSimple (layout, prefs.bg_color);
- widgetStyle = style::Style::create (layout, &styleAttrs);
+ Layout *layout = (Layout*) bw->render_layout;
+ StyleEngine styleEngine (layout);
+
+ styleEngine.startElement ("body");
+ styleEngine.startElement ("pre");
+ widgetStyle = styleEngine.wordStyle ();
+ widgetStyle->ref ();
/* The context menu */
- DW2TB(dw)->connectEvent (&plainReceiver);
+ layout->connectLink (&plainReceiver);
/* Hook destructor to the dw delete call */
dw->setDeleteCallback(a_Plain_free, this);
@@ -125,26 +114,44 @@ DilloPlain::DilloPlain(BrowserWindow *p_bw, const DilloUrl *p_url)
*/
DilloPlain::~DilloPlain()
{
- MSG("::~DilloPlain()\n");
- a_Url_free(url);
+ _MSG("::~DilloPlain()\n");
widgetStyle->unref();
}
/*
* Receive the mouse button press event
*/
-bool DilloPlain::PlainEventReceiver::buttonPress (Widget *widget,
- EventButton *event)
+bool DilloPlain::PlainLinkReceiver::press (Widget *widget, int, int, int, int,
+ EventButton *event)
{
- _MSG("DilloPlain::PlainEventReceiver::buttonPress\n");
+ _MSG("DilloPlain::PlainLinkReceiver::buttonPress\n");
if (event->button == 3) {
- a_UIcmd_page_popup(plain->bw, plain->url, FALSE, FALSE);
+ a_UIcmd_page_popup(plain->bw, FALSE, NULL);
return true;
}
return false;
}
+void DilloPlain::addLine(char *Buf, uint_t BufSize)
+{
+ int len;
+ char buf[128];
+ char *end = Buf + BufSize;
+
+ if (BufSize > 0) {
+ // Limit word length to avoid X11 coordinate
+ // overflow with extremely long lines.
+ while ((len = a_Misc_expand_tabs(&Buf, end, buf, sizeof(buf))))
+ DW2TB(dw)->addText(buf, len, widgetStyle);
+ } else {
+ // Add dummy word for empty lines - otherwise the parbreak is ignored.
+ DW2TB(dw)->addText("", 0, widgetStyle);
+ }
+
+ DW2TB(dw)->addParbreak(0, widgetStyle);
+}
+
/*
* Here we parse plain text and put it into the page structure.
* (This function is called by Plain_callback whenever there's new data)
@@ -152,7 +159,6 @@ bool DilloPlain::PlainEventReceiver::buttonPress (Widget *widget,
void DilloPlain::write(void *Buf, uint_t BufSize, int Eof)
{
char *Start;
- char *data;
uint_t i, len, MaxBytes;
_MSG("DilloPlain::write Eof=%d\n", Eof);
@@ -170,10 +176,7 @@ void DilloPlain::write(void *Buf, uint_t BufSize, int Eof)
}
break;
case ST_Eol:
- data = a_Misc_expand_tabs(Start + i - len, len);
- DW2TB(dw)->addText(data, widgetStyle);
- DW2TB(dw)->addParbreak(0, widgetStyle);
- dFree(data);
+ addLine(Start + i - len, len);
if (Start[i] == '\r' && Start[i + 1] == '\n') ++i;
if (i < MaxBytes) ++i;
state = ST_SeekingEol;
@@ -183,10 +186,7 @@ void DilloPlain::write(void *Buf, uint_t BufSize, int Eof)
}
Start_Ofs += i - len;
if (Eof && len) {
- data = a_Misc_expand_tabs(Start + i - len, len);
- DW2TB(dw)->addText(data, widgetStyle);
- DW2TB(dw)->addParbreak(0, widgetStyle);
- dFree(data);
+ addLine(Start + i - len, len);
Start_Ofs += len;
}
@@ -199,7 +199,7 @@ void DilloPlain::write(void *Buf, uint_t BufSize, int Eof)
void *a_Plain_text(const char *type, void *P, CA_Callback_t *Call, void **Data)
{
DilloWeb *web = (DilloWeb*)P;
- DilloPlain *plain = new DilloPlain(web->bw, web->url);
+ DilloPlain *plain = new DilloPlain(web->bw);
*Call = (CA_Callback_t)Plain_callback;
*Data = (void*)plain;
@@ -209,7 +209,7 @@ void *a_Plain_text(const char *type, void *P, CA_Callback_t *Call, void **Data)
void a_Plain_free(void *data)
{
- MSG("a_Plain_free! %p\n", data);
+ _MSG("a_Plain_free! %p\n", data);
delete ((DilloPlain *)data);
}
diff --git a/src/png.c b/src/png.c
index fa07e781..15f26e3b 100644
--- a/src/png.c
+++ b/src/png.c
@@ -4,6 +4,7 @@
*
* Geoff Lane nov 1999 zzassgl@twirl.mcc.ac.uk
* Luca Rota, Jorge Arellano Cid, Eric Gaudet 2000
+ * Jorge Arellano Cid 2009
*
* "PNG: The Definitive Guide" by Greg Roelofs, O'Reilly
* ISBN 1-56592-542-4
@@ -12,12 +13,8 @@
#include <config.h>
#ifdef ENABLE_PNG
-#include <stdio.h>
-#include <string.h>
#include <stdlib.h> /* For abort() */
-#include <zlib.h>
-
#ifdef HAVE_LIBPNG_PNG_H
#include <libpng/png.h>
#else
@@ -26,10 +23,8 @@
#include "msg.h"
#include "image.hh"
-#include "web.hh"
#include "cache.h"
#include "dicache.h"
-#include "prefs.h"
enum prog_state {
IS_finished, IS_init, IS_nextdata
@@ -58,7 +53,7 @@ static char *prog_state_name[] =
* structure below so that processing can be suspended or resumed at any
* point within an input image.
*
- * In the case of the libpng library, it maintains it's own state in
+ * In the case of the libpng library, it maintains its own state in
* png_ptr and into_ptr so the FSM is very simple - much simpler than the
* ones for XBM and PNM are.
*/
@@ -67,19 +62,19 @@ typedef
struct _DilloPng {
DilloImage *Image; /* Image meta data */
DilloUrl *url; /* Primary Key for the dicache */
- int version; /* Secondary Key for the dicache */
+ int version; /* Secondary Key for the dicache */
double display_exponent; /* gamma correction */
- ulong_t width; /* png image width */
- ulong_t height; /* png image height */
+ ulong_t width; /* png image width */
+ ulong_t height; /* png image height */
png_structp png_ptr; /* libpng private data */
png_infop info_ptr; /* libpng private info */
- uchar_t *image_data; /* decoded image data */
- uchar_t **row_pointers; /* pntr to row starts */
+ uchar_t *image_data; /* decoded image data */
+ uchar_t **row_pointers; /* pntr to row starts */
jmp_buf jmpbuf; /* png error processing */
- int error; /* error flag */
+ int error; /* error flag */
png_uint_32 previous_row;
- int rowbytes; /* No. bytes in image row */
+ int rowbytes; /* No. bytes in image row */
short passes;
short channels; /* No. image channels */
@@ -92,13 +87,13 @@ struct _DilloPng {
* ipbuf ipbufstart ipbufsize
*/
- uchar_t *ipbuf; /* image data in buffer */
- int ipbufstart; /* first valid image byte */
- int ipbufsize; /* size of valid data in */
+ uchar_t *ipbuf; /* image data in buffer */
+ int ipbufstart; /* first valid image byte */
+ int ipbufsize; /* size of valid data in */
enum prog_state state; /* FSM current state */
- uchar_t *linebuf; /* o/p raster data */
+ uchar_t *linebuf; /* o/p raster data */
} DilloPng;
@@ -106,13 +101,6 @@ struct _DilloPng {
#define BLACK 0
#define WHITE 255
-/*
- * Forward declarations
- */
-/* exported function */
-void *a_Png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
- void **Data);
-
static
void Png_error_handling(png_structp png_ptr, png_const_charp msg)
@@ -146,6 +134,15 @@ Png_datainfo_callback(png_structp png_ptr, png_infop info_ptr)
png_get_IHDR(png_ptr, info_ptr, &png->width, &png->height,
&bit_depth, &color_type, &interlace_type, NULL, NULL);
+ /* check max image size */
+ if (png->width <= 0 || png->height <= 0 ||
+ png->width > IMAGE_MAX_AREA / png->height) {
+ MSG("Png_datainfo_callback: suspicious image size request %ldx%ld\n",
+ png->width, png->height);
+ Png_error_handling(png_ptr, "Aborting...");
+ return; /* not reached */
+ }
+
_MSG("Png_datainfo_callback: png->width = %ld\n"
"Png_datainfo_callback: png->height = %ld\n",
png->width, png->height);
@@ -185,7 +182,7 @@ Png_datainfo_callback(png_structp png_ptr, png_infop info_ptr)
png->passes = png_set_interlace_handling(png_ptr);
}
- /* get libpng to update it's state */
+ /* get libpng to update its state */
png_read_update_info(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &png->width, &png->height,
@@ -230,14 +227,15 @@ static void
png_progressive_combine_row(png_ptr, png->row_pointers[row_num], new_row);
+ _MSG("png: row_num=%u previous_row=%u\n", row_num, png->previous_row);
if (row_num < png->previous_row) {
- a_Dicache_new_scan(png->Image, png->url, png->version);
+ a_Dicache_new_scan(png->url, png->version);
}
png->previous_row = row_num;
switch (png->channels) {
case 3:
- a_Dicache_write(png->Image, png->url, png->version,
+ a_Dicache_write(png->url, png->version,
png->image_data + (row_num * png->rowbytes),
(uint_t)row_num);
break;
@@ -275,79 +273,67 @@ static void
data++;
}
}
- a_Dicache_write(png->Image, png->url, png->version,
- png->linebuf, (uint_t)row_num);
+ a_Dicache_write(png->url, png->version, png->linebuf, (uint_t)row_num);
break;
}
default:
- MSG("Png_datarow_callback: unexpected number of channels = %d\n",
- png->channels);
+ MSG("Png_datarow_callback: unexpected number of channels=%d pass=%d\n",
+ png->channels, pass);
abort();
}
}
-static void
- Png_dataend_callback(png_structp png_ptr, png_infop info_ptr)
+static void Png_dataend_callback(png_structp png_ptr, png_infop info_ptr)
{
DilloPng *png;
_MSG("Png_dataend_callback:\n");
+ if (!info_ptr)
+ MSG("Png_dataend_callback: info_ptr = NULL\n");
png = png_get_progressive_ptr(png_ptr);
png->state = IS_finished;
}
-
/*
- * Op: Operation to perform.
- * If (Op == 0)
- * start or continue processing an image if image data exists.
- * else
- * terminate processing, cleanup any allocated memory,
- * close down the decoding process.
- *
- * Client->CbData : pointer to previously allocated DilloPng work area.
- * This holds the current state of the image processing and is saved
- * across calls to this routine.
- * Client->Buf : Pointer to data start.
- * Client->BufSize : the size of the data buffer.
- *
- * You have to keep track of where you are in the image data and
- * how much has been processed.
- *
- * It's entirely possible that you will not see the end of the data. The
- * user may terminate transfer via a Stop button or there may be a network
- * failure. This means that you can't just wait for all the data to be
- * presented before starting conversion and display.
+ * Free up the resources for this image.
*/
-static void Png_callback(int Op, CacheClient_t *Client)
+static void Png_free(DilloPng *png)
{
- DilloPng *png = Client->CbData;
-
- if (Op) {
- /* finished - free up the resources for this image */
- a_Dicache_close(png->url, png->version, Client);
- dFree(png->image_data);
- dFree(png->row_pointers);
- dFree(png->linebuf);
-
- if (setjmp(png->jmpbuf))
- MSG_WARN("PNG: can't destroy read structure\n");
- else if (png->png_ptr)
- png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
- dFree(png);
- return;
- }
+ _MSG("Png_free: png=%p\n", png);
+
+ dFree(png->image_data);
+ dFree(png->row_pointers);
+ dFree(png->linebuf);
+ if (setjmp(png->jmpbuf))
+ MSG_WARN("PNG: can't destroy read structure\n");
+ else if (png->png_ptr)
+ png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
+ dFree(png);
+}
- /* Let's make some sound if we have been called with no data */
- dReturn_if_fail ( Client->Buf != NULL && Client->BufSize > 0 );
+/*
+ * Finish the decoding process (and free the memory)
+ */
+static void Png_close(DilloPng *png, CacheClient_t *Client)
+{
+ _MSG("Png_close\n");
+ /* Let dicache know decoding is over */
+ a_Dicache_close(png->url, png->version, Client);
+ Png_free(png);
+}
- _MSG("Png_callback BufSize = %d\n", Client->BufSize);
+/*
+ * Receive and process new chunks of PNG image data
+ */
+static void Png_write(DilloPng *png, void *Buf, uint_t BufSize)
+{
+ dReturn_if_fail ( Buf != NULL && BufSize > 0 );
/* Keep local copies so we don't have to pass multiple args to
* a number of functions. */
- png->ipbuf = Client->Buf;
- png->ipbufsize = Client->BufSize;
+ png->ipbuf = Buf;
+ png->ipbufsize = BufSize;
/* start/resume the FSM here */
while (png->state != IS_finished && DATASIZE) {
@@ -359,8 +345,7 @@ static void Png_callback(int Op, CacheClient_t *Client)
return; /* need MORE data */
}
/* check the image signature - DON'T update ipbufstart! */
- if (!png_check_sig(png->ipbuf, DATASIZE)) {
- /* you lied to me about it being a PNG image */
+ if (png_sig_cmp(png->ipbuf, 0, DATASIZE)) {
MSG_WARN("\"%s\" is not a PNG file.\n", URL_STR(png->url));
png->state = IS_finished;
break;
@@ -408,11 +393,47 @@ static void Png_callback(int Op, CacheClient_t *Client)
}
/*
+ * Op: Operation to perform.
+ * If (Op == 0)
+ * start or continue processing an image if image data exists.
+ * else
+ * terminate processing, cleanup any allocated memory,
+ * close down the decoding process.
+ *
+ * Client->CbData : pointer to previously allocated DilloPng work area.
+ * This holds the current state of the image processing and is kept
+ * across calls to this routine.
+ * Client->Buf : Pointer to data start.
+ * Client->BufSize : the size of the data buffer.
+ *
+ * You have to keep track of where you are in the image data and
+ * how much has been processed.
+ *
+ * It's entirely possible that you will not see the end of the data. The
+ * user may terminate transfer via a Stop button or there may be a network
+ * failure. This means that you can't just wait for all the data to be
+ * presented before starting conversion and display.
+ */
+void a_Png_callback(int Op, void *data)
+{
+ if (Op == CA_Send) {
+ CacheClient_t *Client = data;
+ Png_write(Client->CbData, Client->Buf, Client->BufSize);
+ } else if (Op == CA_Close) {
+ CacheClient_t *Client = data;
+ Png_close(Client->CbData, Client);
+ } else if (Op == CA_Abort) {
+ Png_free(data);
+ }
+}
+
+/*
* Create the image state data that must be kept between calls
*/
-static DilloPng *Png_new(DilloImage *Image, DilloUrl *url, int version)
+void *a_Png_new(DilloImage *Image, DilloUrl *url, int version)
{
DilloPng *png = dNew0(DilloPng, 1);
+ _MSG("a_Png_new: png=%p\n", png);
png->Image = Image;
png->url = url;
@@ -430,49 +451,9 @@ static DilloPng *Png_new(DilloImage *Image, DilloUrl *url, int version)
return png;
}
-/*
- * MIME handler for "image/png" type
- * (Sets Png_callback or a_Dicache_callback as the cache-client)
- */
-void *a_Png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
- void **Data)
-{
-/*
- * Type: MIME type
- * Ptr: points to a Web structure
- * Call: Dillo calls this with more data/eod
- * Data: raw image data
- */
+#else /* ENABLE_PNG */
- DilloWeb *web = Ptr;
- DICacheEntry *DicEntry;
-
- _MSG("a_Png_image: Type = %s\n"
- "a_Png_image: libpng - Compiled %s; using %s.\n"
- "a_Png_image: zlib - Compiled %s; using %s.\n",
- Type, PNG_LIBPNG_VER_STRING, png_libpng_ver,ZLIB_VERSION,zlib_version);
-
- if (!web->Image)
- web->Image = a_Image_new(0, 0, NULL, prefs.bg_color);
-
- /* Add an extra reference to the Image (for dicache usage) */
- a_Image_ref(web->Image);
-
- DicEntry = a_Dicache_get_entry(web->url);
- if (!DicEntry) {
- /* Let's create an entry for this image... */
- DicEntry = a_Dicache_add_entry(web->url);
-
- /* ... and let the decoder feed it! */
- *Data = Png_new(web->Image, DicEntry->url, DicEntry->version);
- *Call = (CA_Callback_t) Png_callback;
- } else {
- /* Let's feed our client from the dicache */
- a_Dicache_ref(DicEntry->url, DicEntry->version);
- *Data = web->Image;
- *Call = (CA_Callback_t) a_Dicache_callback;
- }
- return (web->Image->dw);
-}
+void *a_Png_new() { return 0; }
+void a_Png_callback() { return; }
#endif /* ENABLE_PNG */
diff --git a/src/prefs.c b/src/prefs.c
index 10e87d0c..5514c01a 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -1,490 +1,120 @@
/*
- * Preferences for dillo
+ * Preferences
*
- * Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
+ * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <string.h> /* for strchr */
-#include <fcntl.h>
-#include <unistd.h>
-#include <locale.h> /* for setlocale */
-#include <ctype.h> /* for isspace */
#include "prefs.h"
-#include "colors.h"
-#include "misc.h"
-#include "msg.h"
-
-#define RCNAME "dillorc"
-
-#define DILLO_START_PAGE "about:splash"
-#define DILLO_HOME "http://www.dillo.org/"
-#define D_GEOMETRY_DEFAULT_WIDTH 780
-#define D_GEOMETRY_DEFAULT_HEIGHT 580
-#define D_GEOMETRY_DEFAULT_XPOS -9999
-#define D_GEOMETRY_DEFAULT_YPOS -9999
-
-#define D_VW_FONTNAME "DejaVu Sans"
-#define D_FW_FONTNAME "DejaVu Sans Mono"
-#define D_SEARCH_URL "http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s"
-#define D_SAVE_DIR "/tmp/"
-#define DW_COLOR_DEFAULT_BGND 0xdcd1ba
-#define DW_COLOR_DEFAULT_TEXT 0x000000
-#define DW_COLOR_DEFAULT_LINK 0x0000ff
-#define DW_COLOR_DEFAULT_VLINK 0x800080
+#define PREFS_START_PAGE "about:splash"
+#define PREFS_HOME "http://www.dillo.org/"
+#define PREFS_FONT_SERIF "DejaVu Serif"
+#define PREFS_FONT_SANS_SERIF "DejaVu Sans"
+#define PREFS_FONT_CURSIVE "URW Chancery L"
+#define PREFS_FONT_FANTASY "DejaVu Sans" /* TODO: find good default */
+#define PREFS_FONT_MONOSPACE "DejaVu Sans Mono"
+#define PREFS_SEARCH_URL "http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s"
+#define PREFS_NO_PROXY "localhost 127.0.0.1"
+#define PREFS_SAVE_DIR "/tmp/"
+#define PREFS_HTTP_REFERER "host"
+#define PREFS_HTTP_USER_AGENT "Dillo/" VERSION
/*-----------------------------------------------------------------------------
* Global Data
*---------------------------------------------------------------------------*/
DilloPrefs prefs;
-/*-----------------------------------------------------------------------------
- * Local types
- *---------------------------------------------------------------------------*/
-
-/* define enumeration values to be returned for specific symbols */
-typedef enum {
- DRC_TOKEN_ALLOW_WHITE_BG,
- DRC_TOKEN_BG_COLOR,
- DRC_TOKEN_CONTRAST_VISITED_COLOR,
- DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT,
- DRC_TOKEN_FOCUS_NEW_TAB,
- DRC_TOKEN_FONT_FACTOR,
- DRC_TOKEN_FORCE_MY_COLORS,
- DRC_TOKEN_FULLWINDOW_START,
- DRC_TOKEN_FW_FONT,
- DRC_TOKEN_GENERATE_SUBMIT,
- DRC_TOKEN_GEOMETRY,
- DRC_TOKEN_HOME,
- DRC_TOKEN_LIMIT_TEXT_WIDTH,
- DRC_TOKEN_LINK_COLOR,
- DRC_TOKEN_LOAD_IMAGES,
- DRC_TOKEN_BUFFERED_DRAWING,
- DRC_TOKEN_MIDDLE_CLICK_OPENS_NEW_TAB,
- DRC_TOKEN_NOPROXY,
- DRC_TOKEN_PANEL_SIZE,
- DRC_TOKEN_PROXY,
- DRC_TOKEN_PROXYUSER,
- DRC_TOKEN_REFERER,
- DRC_TOKEN_SAVE_DIR,
- DRC_TOKEN_SEARCH_URL,
- DRC_TOKEN_SHOW_BACK,
- DRC_TOKEN_SHOW_BOOKMARKS,
- DRC_TOKEN_SHOW_CLEAR_URL,
- DRC_TOKEN_SHOW_EXTRA_WARNINGS,
- DRC_TOKEN_SHOW_FORW,
- DRC_TOKEN_SHOW_HOME,
- DRC_TOKEN_SHOW_FILEMENU,
- DRC_TOKEN_SHOW_MSG,
- DRC_TOKEN_SHOW_PROGRESS_BOX,
- DRC_TOKEN_SHOW_RELOAD,
- DRC_TOKEN_SHOW_SAVE,
- DRC_TOKEN_SHOW_SEARCH,
- DRC_TOKEN_SHOW_STOP,
- DRC_TOKEN_SHOW_TOOLTIP,
- DRC_TOKEN_SHOW_URL,
- DRC_TOKEN_SMALL_ICONS,
- DRC_TOKEN_STANDARD_WIDGET_COLORS,
- DRC_TOKEN_START_PAGE,
- DRC_TOKEN_TEXT_COLOR,
- DRC_TOKEN_VISITED_COLOR,
- DRC_TOKEN_VW_FONT,
- DRC_TOKEN_W3C_PLUS_HEURISTICS
-} RcToken_t;
-
-typedef struct SymNode_ SymNode_t;
-
-struct SymNode_ {
- char *name;
- RcToken_t token;
-};
-
-/*-----------------------------------------------------------------------------
- * Local data
- *---------------------------------------------------------------------------*/
-
-/* Symbol array, sorted alphabetically */
-static const SymNode_t symbols[] = {
- { "allow_white_bg", DRC_TOKEN_ALLOW_WHITE_BG },
- { "bg_color", DRC_TOKEN_BG_COLOR },
- { "buffered_drawing", DRC_TOKEN_BUFFERED_DRAWING },
- { "contrast_visited_color", DRC_TOKEN_CONTRAST_VISITED_COLOR },
- { "enterpress_forces_submit", DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT },
- { "focus_new_tab", DRC_TOKEN_FOCUS_NEW_TAB },
- { "font_factor", DRC_TOKEN_FONT_FACTOR },
- { "force_my_colors", DRC_TOKEN_FORCE_MY_COLORS },
- { "fullwindow_start", DRC_TOKEN_FULLWINDOW_START },
- { "fw_fontname", DRC_TOKEN_FW_FONT },
- { "generate_submit", DRC_TOKEN_GENERATE_SUBMIT },
- { "geometry", DRC_TOKEN_GEOMETRY },
- { "home", DRC_TOKEN_HOME },
- { "http_proxy", DRC_TOKEN_PROXY },
- { "http_proxyuser", DRC_TOKEN_PROXYUSER },
- { "http_referer", DRC_TOKEN_REFERER },
- { "limit_text_width", DRC_TOKEN_LIMIT_TEXT_WIDTH },
- { "link_color", DRC_TOKEN_LINK_COLOR },
- { "load_images", DRC_TOKEN_LOAD_IMAGES },
- { "middle_click_opens_new_tab", DRC_TOKEN_MIDDLE_CLICK_OPENS_NEW_TAB },
- { "no_proxy", DRC_TOKEN_NOPROXY },
- { "panel_size", DRC_TOKEN_PANEL_SIZE },
- { "save_dir", DRC_TOKEN_SAVE_DIR },
- { "search_url", DRC_TOKEN_SEARCH_URL },
- { "show_back", DRC_TOKEN_SHOW_BACK },
- { "show_bookmarks", DRC_TOKEN_SHOW_BOOKMARKS },
- { "show_clear_url", DRC_TOKEN_SHOW_CLEAR_URL },
- { "show_extra_warnings", DRC_TOKEN_SHOW_EXTRA_WARNINGS },
- { "show_filemenu", DRC_TOKEN_SHOW_FILEMENU },
- { "show_forw", DRC_TOKEN_SHOW_FORW },
- { "show_home", DRC_TOKEN_SHOW_HOME },
- { "show_msg", DRC_TOKEN_SHOW_MSG },
- { "show_progress_box", DRC_TOKEN_SHOW_PROGRESS_BOX },
- { "show_reload", DRC_TOKEN_SHOW_RELOAD },
- { "show_save", DRC_TOKEN_SHOW_SAVE },
- { "show_search", DRC_TOKEN_SHOW_SEARCH },
- { "show_stop", DRC_TOKEN_SHOW_STOP },
- { "show_tooltip", DRC_TOKEN_SHOW_TOOLTIP },
- { "show_url", DRC_TOKEN_SHOW_URL },
- { "small_icons", DRC_TOKEN_SMALL_ICONS },
- { "standard_widget_colors", DRC_TOKEN_STANDARD_WIDGET_COLORS },
- { "start_page", DRC_TOKEN_START_PAGE },
- { "text_color", DRC_TOKEN_TEXT_COLOR },
- { "visited_color", DRC_TOKEN_VISITED_COLOR, },
- { "vw_fontname", DRC_TOKEN_VW_FONT },
- { "w3c_plus_heuristics", DRC_TOKEN_W3C_PLUS_HEURISTICS }
-};
-
-static const uint_t n_symbols = sizeof (symbols) / sizeof (symbols[0]);
-
/*
- *- Mini parser -------------------------------------------------------------
+ * Sets the default settings.
*/
-/*
- * Comparison function for binary search
- */
-static int Prefs_symbol_cmp(const void *a, const void *b)
-{
- return strcmp(((SymNode_t*)a)->name, ((SymNode_t*)b)->name);
-}
-
-/*
- * Parse a name/value pair and set preferences accordingly.
- */
-static int Prefs_parse_pair(char *name, char *value)
-{
- int st;
- SymNode_t key, *node;
-
- key.name = name;
- node = bsearch(&key, symbols, n_symbols,
- sizeof(SymNode_t), Prefs_symbol_cmp);
- if (!node) {
- MSG("prefs: {%s} is not a recognized token.\n", name);
- return -1;
- }
-
- switch (node->token) {
- case DRC_TOKEN_GEOMETRY:
- a_Misc_parse_geometry(value, &prefs.xpos, &prefs.ypos,
- &prefs.width, &prefs.height);
- break;
- case DRC_TOKEN_PROXY:
- a_Url_free(prefs.http_proxy);
- prefs.http_proxy = a_Url_new(value, NULL);
- break;
- case DRC_TOKEN_PROXYUSER:
- dFree(prefs.http_proxyuser);
- prefs.http_proxyuser = dStrdup(value);
- break;
- case DRC_TOKEN_REFERER:
- dFree(prefs.http_referer);
- prefs.http_referer = dStrdup(value);
- break;
- case DRC_TOKEN_NOPROXY:
- dFree(prefs.no_proxy);
- prefs.no_proxy = dStrdup(value);
- break;
- case DRC_TOKEN_LINK_COLOR:
- prefs.link_color = a_Color_parse(value, prefs.link_color, &st);
- break;
- case DRC_TOKEN_VISITED_COLOR:
- prefs.visited_color = a_Color_parse(value, prefs.visited_color, &st);
- break;
- case DRC_TOKEN_TEXT_COLOR:
- prefs.text_color = a_Color_parse(value, prefs.text_color, &st);
- break;
- case DRC_TOKEN_BG_COLOR:
- prefs.bg_color = a_Color_parse(value, prefs.bg_color, &st);
- break;
- case DRC_TOKEN_ALLOW_WHITE_BG:
- prefs.allow_white_bg = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_FORCE_MY_COLORS:
- prefs.force_my_colors = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_CONTRAST_VISITED_COLOR:
- prefs.contrast_visited_color = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_STANDARD_WIDGET_COLORS:
- prefs.standard_widget_colors = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_PANEL_SIZE:
- if (!dStrcasecmp(value, "tiny"))
- prefs.panel_size = P_tiny;
- else if (!dStrcasecmp(value, "small"))
- prefs.panel_size = P_small;
- else if (!dStrcasecmp(value, "medium"))
- prefs.panel_size = P_medium;
- else /* default to "large" */
- prefs.panel_size = P_large;
- break;
- case DRC_TOKEN_SMALL_ICONS:
- prefs.small_icons = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_START_PAGE:
- a_Url_free(prefs.start_page);
- prefs.start_page = a_Url_new(value, NULL);
- break;
- case DRC_TOKEN_HOME:
- a_Url_free(prefs.home);
- prefs.home = a_Url_new(value, NULL);
- break;
- case DRC_TOKEN_SHOW_TOOLTIP:
- prefs.show_tooltip = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_FOCUS_NEW_TAB:
- prefs.focus_new_tab = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_FONT_FACTOR:
- prefs.font_factor = strtod(value, NULL);
- break;
- case DRC_TOKEN_LIMIT_TEXT_WIDTH:
- prefs.limit_text_width = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_W3C_PLUS_HEURISTICS:
- prefs.w3c_plus_heuristics = (strcmp(value,"YES") == 0);
- break;
- case DRC_TOKEN_SHOW_BACK:
- prefs.show_back = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_FORW:
- prefs.show_forw = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_HOME:
- prefs.show_home = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_RELOAD:
- prefs.show_reload = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_SAVE:
- prefs.show_save = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_STOP:
- prefs.show_stop = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_BOOKMARKS:
- prefs.show_bookmarks = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_FILEMENU:
- prefs.show_filemenu = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_CLEAR_URL:
- prefs.show_clear_url = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_URL:
- prefs.show_url = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_SEARCH:
- prefs.show_search = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_PROGRESS_BOX:
- prefs.show_progress_box = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_FULLWINDOW_START:
- prefs.fullwindow_start = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_LOAD_IMAGES:
- prefs.load_images = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_BUFFERED_DRAWING:
- prefs.buffered_drawing = atoi(value);
- break;
- case DRC_TOKEN_FW_FONT:
- dFree(prefs.fw_fontname);
- prefs.fw_fontname = dStrdup(value);
- break;
- case DRC_TOKEN_VW_FONT:
- dFree(prefs.vw_fontname);
- prefs.vw_fontname = dStrdup(value);
- break;
- case DRC_TOKEN_GENERATE_SUBMIT:
- prefs.generate_submit = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT:
- prefs.enterpress_forces_submit = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_MIDDLE_CLICK_OPENS_NEW_TAB:
- prefs.middle_click_opens_new_tab = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SEARCH_URL:
- dFree(prefs.search_url);
- prefs.search_url = dStrdup(value);
- break;
- case DRC_TOKEN_SAVE_DIR:
- dFree(prefs.save_dir);
- prefs.save_dir = dStrdup(value);
- case DRC_TOKEN_SHOW_MSG:
- prefs.show_msg = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_EXTRA_WARNINGS:
- prefs.show_extra_warnings = (strcmp(value, "YES") == 0);
- break;
- default:
- MSG_WARN("prefs: {%s} IS recognized but not handled!\n", name);
- break; /* Not reached */
- }
-
- return 0;
-}
-
-/*
- * Parse dillorc and set the values in the prefs structure.
- */
-static int Prefs_parse_dillorc(void)
-{
- FILE *F_in;
- char *filename, *line, *name, *value;
- int ret = -1;
-
- filename = dStrconcat(dGethomedir(), "/.dillo/", RCNAME, NULL);
- if (!(F_in = fopen(filename, "r"))) {
- MSG("prefs: Can't open %s file: %s\n", RCNAME, filename);
- if (!(F_in = fopen(DILLORC_SYS, "r"))) {
- MSG("prefs: Can't open %s file: %s\n", RCNAME, DILLORC_SYS);
- MSG("prefs: Using internal defaults.\n");
- } else {
- MSG("prefs: Using %s\n", DILLORC_SYS);
- }
- }
-
- if (F_in) {
- /* scan dillorc line by line */
- while ((line = dGetline(F_in)) != NULL) {
- if (dParser_get_rc_pair(&line, &name, &value) == 0) {
- _MSG("{%s}, {%s}\n", name, value);
- Prefs_parse_pair(name, value);
- } else if (line[0] && line[0] != '#' && (!name || !value)) {
- MSG("prefs: Syntax error in %s: name=\"%s\" value=\"%s\"\n",
- RCNAME, name, value);
- }
- dFree(line);
- }
- fclose(F_in);
- ret = 0;
- }
- dFree(filename);
-
- return ret;
-}
-
-/*---------------------------------------------------------------------------*/
-
void a_Prefs_init(void)
{
- char *old_locale;
-
- prefs.width = D_GEOMETRY_DEFAULT_WIDTH;
- prefs.height = D_GEOMETRY_DEFAULT_HEIGHT;
- prefs.xpos = D_GEOMETRY_DEFAULT_XPOS;
- prefs.ypos = D_GEOMETRY_DEFAULT_YPOS;
- prefs.http_proxy = NULL;
- prefs.http_proxyuser = NULL;
- prefs.http_referer = dStrdup("host");
- prefs.no_proxy = NULL;
- prefs.link_color = DW_COLOR_DEFAULT_LINK;
- prefs.visited_color = DW_COLOR_DEFAULT_VLINK;
- prefs.bg_color = DW_COLOR_DEFAULT_BGND;
- prefs.text_color = DW_COLOR_DEFAULT_TEXT;
- prefs.start_page = a_Url_new(DILLO_START_PAGE, NULL);
- prefs.home = a_Url_new(DILLO_HOME, NULL);
prefs.allow_white_bg = TRUE;
- prefs.force_my_colors = FALSE;
+ prefs.buffered_drawing = 1;
prefs.contrast_visited_color = TRUE;
- prefs.standard_widget_colors = FALSE;
- prefs.show_tooltip = TRUE;
- prefs.panel_size = P_large;
- prefs.small_icons = FALSE;
- prefs.limit_text_width = FALSE;
- prefs.w3c_plus_heuristics = TRUE;
+ prefs.enterpress_forces_submit = FALSE;
+ prefs.filter_auto_requests = PREFS_FILTER_SAME_DOMAIN;
prefs.focus_new_tab = TRUE;
+ prefs.font_cursive = dStrdup(PREFS_FONT_CURSIVE);
prefs.font_factor = 1.0;
- prefs.show_back=TRUE;
- prefs.show_forw=TRUE;
- prefs.show_home=TRUE;
- prefs.show_reload=TRUE;
- prefs.show_save=TRUE;
- prefs.show_stop=TRUE;
- prefs.show_bookmarks=TRUE;
- prefs.show_filemenu=TRUE;
- prefs.show_clear_url=TRUE;
- prefs.show_url=TRUE;
- prefs.show_search=TRUE;
- prefs.show_progress_box=TRUE;
- prefs.fullwindow_start=FALSE;
+ prefs.font_max_size = 100;
+ prefs.font_min_size = 6;
+ prefs.font_fantasy = dStrdup(PREFS_FONT_FANTASY);
+ prefs.font_monospace = dStrdup(PREFS_FONT_MONOSPACE);
+ prefs.font_sans_serif = dStrdup(PREFS_FONT_SANS_SERIF);
+ prefs.font_serif = dStrdup(PREFS_FONT_SERIF);
+ prefs.fullwindow_start = FALSE;
+
+ /* these four constitute the geometry */
+ prefs.width = PREFS_GEOMETRY_DEFAULT_WIDTH;
+ prefs.height = PREFS_GEOMETRY_DEFAULT_HEIGHT;
+ prefs.xpos = PREFS_GEOMETRY_DEFAULT_XPOS;
+ prefs.ypos = PREFS_GEOMETRY_DEFAULT_YPOS;
+
+ prefs.home = a_Url_new(PREFS_HOME, NULL);
+ prefs.http_language = NULL;
+ prefs.http_proxy = NULL;
+ prefs.http_max_conns = 6;
+ prefs.http_proxyuser = NULL;
+ prefs.http_referer = dStrdup(PREFS_HTTP_REFERER);
+ prefs.http_user_agent = dStrdup(PREFS_HTTP_USER_AGENT);
+ prefs.limit_text_width = FALSE;
prefs.load_images=TRUE;
- prefs.buffered_drawing=1;
- prefs.vw_fontname = dStrdup(D_VW_FONTNAME);
- prefs.fw_fontname = dStrdup(D_FW_FONTNAME);
- prefs.generate_submit = FALSE;
- prefs.enterpress_forces_submit = FALSE;
+ prefs.load_stylesheets=TRUE;
+ prefs.middle_click_drags_page = TRUE;
prefs.middle_click_opens_new_tab = TRUE;
- prefs.search_url = dStrdup(D_SEARCH_URL);
- prefs.save_dir = dStrdup(D_SAVE_DIR);
- prefs.show_msg = TRUE;
+ prefs.no_proxy = dStrdup(PREFS_NO_PROXY);
+ prefs.panel_size = P_medium;
+ prefs.parse_embedded_css=TRUE;
+ prefs.save_dir = dStrdup(PREFS_SAVE_DIR);
+ prefs.search_url = dStrdup(PREFS_SEARCH_URL);
+ prefs.show_back = TRUE;
+ prefs.show_bookmarks = TRUE;
+ prefs.show_clear_url = TRUE;
prefs.show_extra_warnings = FALSE;
-
- /* this locale stuff is to avoid parsing problems with float numbers */
- old_locale = dStrdup (setlocale (LC_NUMERIC, NULL));
- setlocale (LC_NUMERIC, "C");
-
- Prefs_parse_dillorc();
-
- setlocale (LC_NUMERIC, old_locale);
- dFree (old_locale);
-
+ prefs.show_filemenu=TRUE;
+ prefs.show_forw = TRUE;
+ prefs.show_help = TRUE;
+ prefs.show_home = TRUE;
+ prefs.show_msg = TRUE;
+ prefs.show_progress_box = TRUE;
+ prefs.show_reload = TRUE;
+ prefs.show_save = TRUE;
+ prefs.show_search = TRUE;
+ prefs.show_stop = TRUE;
+ prefs.show_tools = TRUE;
+ prefs.show_tooltip = TRUE;
+ prefs.show_url = TRUE;
+ prefs.small_icons = FALSE;
+ prefs.start_page = a_Url_new(PREFS_START_PAGE, NULL);
+ prefs.w3c_plus_heuristics = TRUE;
}
/*
- * Preferences memory-deallocation
+ * memory-deallocation
* (Call this one at exit time)
*/
void a_Prefs_freeall(void)
{
+ dFree(prefs.font_cursive);
+ dFree(prefs.font_fantasy);
+ dFree(prefs.font_monospace);
+ dFree(prefs.font_sans_serif);
+ dFree(prefs.font_serif);
+ a_Url_free(prefs.home);
+ dFree(prefs.http_language);
+ a_Url_free(prefs.http_proxy);
dFree(prefs.http_proxyuser);
dFree(prefs.http_referer);
+ dFree(prefs.http_user_agent);
dFree(prefs.no_proxy);
- a_Url_free(prefs.http_proxy);
- dFree(prefs.fw_fontname);
- dFree(prefs.vw_fontname);
- a_Url_free(prefs.start_page);
- a_Url_free(prefs.home);
- dFree(prefs.search_url);
dFree(prefs.save_dir);
+ dFree(prefs.search_url);
+ a_Url_free(prefs.start_page);
}
diff --git a/src/prefs.h b/src/prefs.h
index 7a409631..684262ed 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -1,3 +1,14 @@
+/*
+ * Preferences
+ *
+ * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
#ifndef __PREFS_H__
#define __PREFS_H__
@@ -7,9 +18,17 @@
extern "C" {
#endif /* __cplusplus */
+#define PREFS_GEOMETRY_DEFAULT_WIDTH 780
+#define PREFS_GEOMETRY_DEFAULT_HEIGHT 580
+#define PREFS_GEOMETRY_DEFAULT_XPOS -9999
+#define PREFS_GEOMETRY_DEFAULT_YPOS -9999
+
/* Panel sizes */
enum { P_tiny = 0, P_small, P_medium, P_large };
+enum {PREFS_FILTER_ALLOW_ALL,
+ PREFS_FILTER_SAME_DOMAIN};
+
typedef struct _DilloPrefs DilloPrefs;
struct _DilloPrefs {
@@ -17,20 +36,17 @@ struct _DilloPrefs {
int height;
int xpos;
int ypos;
+ char *http_language;
+ int32_t http_max_conns;
DilloUrl *http_proxy;
char *http_proxyuser;
char *http_referer;
+ char *http_user_agent;
char *no_proxy;
DilloUrl *start_page;
DilloUrl *home;
- int32_t link_color;
- int32_t visited_color;
- int32_t bg_color;
- int32_t text_color;
bool_t allow_white_bg;
- bool_t force_my_colors;
bool_t contrast_visited_color;
- bool_t standard_widget_colors;
bool_t show_tooltip;
int panel_size;
bool_t small_icons;
@@ -38,6 +54,8 @@ struct _DilloPrefs {
bool_t w3c_plus_heuristics;
bool_t focus_new_tab;
double font_factor;
+ int32_t font_max_size;
+ int32_t font_min_size;
bool_t show_back;
bool_t show_forw;
bool_t show_home;
@@ -45,23 +63,31 @@ struct _DilloPrefs {
bool_t show_save;
bool_t show_stop;
bool_t show_bookmarks;
+ bool_t show_tools;
bool_t show_filemenu;
bool_t show_clear_url;
bool_t show_url;
bool_t show_search;
+ bool_t show_help;
bool_t show_progress_box;
bool_t fullwindow_start;
bool_t load_images;
+ bool_t load_stylesheets;
+ bool_t parse_embedded_css;
+ int filter_auto_requests;
int32_t buffered_drawing;
- char *vw_fontname;
- char *fw_fontname;
- bool_t generate_submit;
+ char *font_serif;
+ char *font_sans_serif;
+ char *font_cursive;
+ char *font_fantasy;
+ char *font_monospace;
bool_t enterpress_forces_submit;
bool_t middle_click_opens_new_tab;
char *search_url;
char *save_dir;
bool_t show_msg;
bool_t show_extra_warnings;
+ bool_t middle_click_drags_page;
};
/* Global Data */
diff --git a/src/prefsparser.cc b/src/prefsparser.cc
new file mode 100644
index 00000000..78cade0b
--- /dev/null
+++ b/src/prefsparser.cc
@@ -0,0 +1,208 @@
+/*
+ * Preferences parser
+ *
+ * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <locale.h> /* for setlocale */
+
+#include "prefs.h"
+#include "misc.h"
+#include "msg.h"
+
+#include "prefsparser.hh"
+
+typedef enum {
+ PREFS_BOOL,
+ PREFS_STRING,
+ PREFS_URL,
+ PREFS_INT32,
+ PREFS_DOUBLE,
+ PREFS_GEOMETRY,
+ PREFS_FILTER,
+ PREFS_PANEL_SIZE
+} PrefType_t;
+
+typedef struct SymNode_ {
+ const char *name;
+ void *pref;
+ PrefType_t type;
+} SymNode_t;
+
+/*
+ * Parse a name/value pair and set preferences accordingly.
+ */
+int PrefsParser::parseOption(char *name, char *value)
+{
+ const SymNode_t *node;
+ uint_t i;
+
+ /* Symbol array, sorted alphabetically */
+ const SymNode_t symbols[] = {
+ { "allow_white_bg", &prefs.allow_white_bg, PREFS_BOOL },
+ { "buffered_drawing", &prefs.buffered_drawing, PREFS_INT32 },
+ { "contrast_visited_color", &prefs.contrast_visited_color, PREFS_BOOL },
+ { "enterpress_forces_submit", &prefs.enterpress_forces_submit,
+ PREFS_BOOL },
+ { "filter_auto_requests", &prefs.filter_auto_requests, PREFS_FILTER },
+ { "focus_new_tab", &prefs.focus_new_tab, PREFS_BOOL },
+ { "font_cursive", &prefs.font_cursive, PREFS_STRING },
+ { "font_factor", &prefs.font_factor, PREFS_DOUBLE },
+ { "font_fantasy", &prefs.font_fantasy, PREFS_STRING },
+ { "font_max_size", &prefs.font_max_size, PREFS_INT32 },
+ { "font_min_size", &prefs.font_min_size, PREFS_INT32 },
+ { "font_monospace", &prefs.font_monospace, PREFS_STRING },
+ { "font_sans_serif", &prefs.font_sans_serif, PREFS_STRING },
+ { "font_serif", &prefs.font_serif, PREFS_STRING },
+ { "fullwindow_start", &prefs.fullwindow_start, PREFS_BOOL },
+ { "geometry", NULL, PREFS_GEOMETRY },
+ { "home", &prefs.home, PREFS_URL },
+ { "http_language", &prefs.http_language, PREFS_STRING },
+ { "http_max_conns", &prefs.http_max_conns, PREFS_INT32 },
+ { "http_proxy", &prefs.http_proxy, PREFS_URL },
+ { "http_proxyuser", &prefs.http_proxyuser, PREFS_STRING },
+ { "http_referer", &prefs.http_referer, PREFS_STRING },
+ { "http_user_agent", &prefs.http_user_agent, PREFS_STRING },
+ { "limit_text_width", &prefs.limit_text_width, PREFS_BOOL },
+ { "load_images", &prefs.load_images, PREFS_BOOL },
+ { "load_stylesheets", &prefs.load_stylesheets, PREFS_BOOL },
+ { "middle_click_drags_page", &prefs.middle_click_drags_page,
+ PREFS_BOOL },
+ { "middle_click_opens_new_tab", &prefs.middle_click_opens_new_tab,
+ PREFS_BOOL },
+ { "no_proxy", &prefs.no_proxy, PREFS_STRING },
+ { "panel_size", &prefs.panel_size, PREFS_PANEL_SIZE },
+ { "parse_embedded_css", &prefs.parse_embedded_css, PREFS_BOOL },
+ { "save_dir", &prefs.save_dir, PREFS_STRING },
+ { "search_url", &prefs.search_url, PREFS_STRING },
+ { "show_back", &prefs.show_back, PREFS_BOOL },
+ { "show_bookmarks", &prefs.show_bookmarks, PREFS_BOOL },
+ { "show_clear_url", &prefs.show_clear_url, PREFS_BOOL },
+ { "show_extra_warnings", &prefs.show_extra_warnings, PREFS_BOOL },
+ { "show_filemenu", &prefs.show_filemenu, PREFS_BOOL },
+ { "show_forw", &prefs.show_forw, PREFS_BOOL },
+ { "show_help", &prefs.show_help, PREFS_BOOL },
+ { "show_home", &prefs.show_home, PREFS_BOOL },
+ { "show_msg", &prefs.show_msg, PREFS_BOOL },
+ { "show_progress_box", &prefs.show_progress_box, PREFS_BOOL },
+ { "show_reload", &prefs.show_reload, PREFS_BOOL },
+ { "show_save", &prefs.show_save, PREFS_BOOL },
+ { "show_search", &prefs.show_search, PREFS_BOOL },
+ { "show_stop", &prefs.show_stop, PREFS_BOOL },
+ { "show_tools", &prefs.show_tools, PREFS_BOOL },
+ { "show_tooltip", &prefs.show_tooltip, PREFS_BOOL },
+ { "show_url", &prefs.show_url, PREFS_BOOL },
+ { "small_icons", &prefs.small_icons, PREFS_BOOL },
+ { "start_page", &prefs.start_page, PREFS_URL },
+ { "w3c_plus_heuristics", &prefs.w3c_plus_heuristics, PREFS_BOOL }
+ };
+
+ node = NULL;
+ for (i = 0; i < sizeof(symbols) / sizeof(SymNode_t); i++) {
+ if (!strcmp(symbols[i].name, name)) {
+ node = & (symbols[i]);
+ break;
+ }
+ }
+
+ if (!node) {
+ MSG("prefs: {%s} is not a recognized token.\n", name);
+ return -1;
+ }
+
+ switch (node->type) {
+ case PREFS_BOOL:
+ *(bool_t *)node->pref = (!dStrcasecmp(value, "yes") ||
+ !dStrcasecmp(value, "true"));
+ break;
+ case PREFS_STRING:
+ dFree(*(char **)node->pref);
+ *(char **)node->pref = dStrdup(value);
+ break;
+ case PREFS_URL:
+ a_Url_free(*(DilloUrl **)node->pref);
+ *(DilloUrl **)node->pref = a_Url_new(value, NULL);
+ break;
+ case PREFS_INT32:
+ *(int32_t *)node->pref = strtol(value, NULL, 10);
+ break;
+ case PREFS_DOUBLE:
+ *(double *)node->pref = strtod(value, NULL);
+ break;
+ case PREFS_GEOMETRY:
+ a_Misc_parse_geometry(value, &prefs.xpos, &prefs.ypos,
+ &prefs.width, &prefs.height);
+ break;
+ case PREFS_FILTER:
+ if (!dStrcasecmp(value, "same_domain"))
+ prefs.filter_auto_requests = PREFS_FILTER_SAME_DOMAIN;
+ else {
+ if (dStrcasecmp(value, "allow_all"))
+ MSG_WARN("prefs: unrecognized value for filter_auto_requests\n");
+ prefs.filter_auto_requests = PREFS_FILTER_ALLOW_ALL;
+ }
+ break;
+ case PREFS_PANEL_SIZE:
+ if (!dStrcasecmp(value, "tiny"))
+ prefs.panel_size = P_tiny;
+ else if (!dStrcasecmp(value, "small"))
+ prefs.panel_size = P_small;
+ else if (!dStrcasecmp(value, "large"))
+ prefs.panel_size = P_large;
+ else /* default to "medium" */
+ prefs.panel_size = P_medium;
+ break;
+ default:
+ MSG_WARN("prefs: {%s} IS recognized but not handled!\n", name);
+ break; /* Not reached */
+ }
+
+ if (prefs.limit_text_width) {
+ /* BUG: causes 100% CPU usage with <button> or <input type="image"> */
+ MSG_WARN("Disabling limit_text_width preference (currently broken).\n");
+ prefs.limit_text_width = FALSE;
+ }
+
+ return 0;
+}
+
+/*
+ * Parses the dillorc and sets the values in the prefs structure.
+ */
+void PrefsParser::parse(FILE *fp)
+{
+ char *line, *name, *value, *oldLocale;
+ int st;
+
+ // changing the LC_NUMERIC locale (temporarily) to C
+ // avoids parsing problems with float numbers
+ oldLocale = dStrdup(setlocale(LC_NUMERIC, NULL));
+ setlocale(LC_NUMERIC, "C");
+
+ // scan the file line by line
+ while ((line = dGetline(fp)) != NULL) {
+ st = dParser_parse_rc_line(&line, &name, &value);
+
+ if (st == 0) {
+ _MSG("prefsparser: name=%s, value=%s\n", name, value);
+ parseOption(name, value);
+ } else if (st < 0) {
+ MSG_ERR("prefsparser: Syntax error in dillorc:"
+ " name=\"%s\" value=\"%s\"\n", name, value);
+ }
+
+ dFree(line);
+ }
+ fclose(fp);
+
+ // restore the old numeric locale
+ setlocale(LC_NUMERIC, oldLocale);
+ dFree(oldLocale);
+}
diff --git a/src/prefsparser.hh b/src/prefsparser.hh
new file mode 100644
index 00000000..d10c43c4
--- /dev/null
+++ b/src/prefsparser.hh
@@ -0,0 +1,24 @@
+/*
+ * Preferences parser
+ *
+ * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PREFS_HH__
+#define __PREFS_HH__
+
+#ifdef __cplusplus
+class PrefsParser {
+public:
+ static int parseOption(char *name, char *value);
+ static int parseLine(char *line, char *name, char *value);
+ static void parse(FILE *fp);
+};
+#endif /* __cplusplus */
+
+#endif /* __PREFS_HH__ */
diff --git a/src/styleengine.cc b/src/styleengine.cc
new file mode 100644
index 00000000..906f47ee
--- /dev/null
+++ b/src/styleengine.cc
@@ -0,0 +1,678 @@
+/*
+ * File: styleengine.cc
+ *
+ * Copyright 2008-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "../dlib/dlib.h"
+#include "msg.h"
+#include "prefs.h"
+#include "html_common.hh"
+#include "styleengine.hh"
+
+using namespace dw::core::style;
+
+StyleEngine::StyleEngine (dw::core::Layout *layout) {
+ StyleAttrs style_attrs;
+ FontAttrs font_attrs;
+
+ doctree = new Doctree ();
+ stack = new lout::misc::SimpleVector <Node> (1);
+ cssContext = new CssContext ();
+ this->layout = layout;
+ importDepth = 0;
+
+ stack->increase ();
+ Node *n = stack->getRef (stack->size () - 1);
+
+ /* Create a dummy font, attribute, and tag for the bottom of the stack. */
+ font_attrs.name = prefs.font_sans_serif;
+ font_attrs.size = (int) (14 * prefs.font_factor + 0.5);
+ if (font_attrs.size < prefs.font_min_size)
+ font_attrs.size = prefs.font_min_size;
+ if (font_attrs.size > prefs.font_max_size)
+ font_attrs.size = prefs.font_max_size;
+ font_attrs.weight = 400;
+ font_attrs.style = FONT_STYLE_NORMAL;
+ font_attrs.letterSpacing = 0;
+
+ style_attrs.initValues ();
+ style_attrs.font = Font::create (layout, &font_attrs);
+ style_attrs.color = Color::create (layout, 0);
+ style_attrs.backgroundColor = Color::create (layout, 0xffffff);
+
+ n->style = Style::create (layout, &style_attrs);
+ n->wordStyle = NULL;
+ n->styleAttribute = NULL;
+ n->inheritBackgroundColor = false;
+}
+
+StyleEngine::~StyleEngine () {
+ while (doctree->top ())
+ endElement (doctree->top ()->element);
+ delete stack;
+ delete doctree;
+ delete cssContext;
+}
+
+/**
+ * \brief tell the styleEngine that a new html element has started.
+ */
+void StyleEngine::startElement (int element) {
+ if (stack->getRef (stack->size () - 1)->style == NULL)
+ style0 ();
+
+ stack->increase ();
+ Node *n = stack->getRef (stack->size () - 1);
+ n->style = NULL;
+ n->wordStyle = NULL;
+ n->styleAttribute = NULL;
+ n->inheritBackgroundColor = false;
+
+ DoctreeNode *dn = doctree->push ();
+ dn->element = element;
+}
+
+void StyleEngine::startElement (const char *tagname) {
+ startElement (a_Html_tag_index (tagname));
+}
+
+void StyleEngine::setId (const char *id) {
+ DoctreeNode *dn = doctree->top ();
+ assert (dn->id == NULL);
+ dn->id = dStrdup (id);
+};
+
+/**
+ * \brief split a string at sep chars and return a SimpleVector of strings
+ */
+static lout::misc::SimpleVector<char *> *splitStr (const char *str, char sep) {
+ const char *p1 = NULL;
+ lout::misc::SimpleVector<char *> *list =
+ new lout::misc::SimpleVector<char *> (1);
+
+ for (;; str++) {
+ if (*str != '\0' && *str != sep) {
+ if (!p1)
+ p1 = str;
+ } else if (p1) {
+ list->increase ();
+ list->set (list->size () - 1, dStrndup (p1, str - p1));
+ p1 = NULL;
+ }
+
+ if (*str == '\0')
+ break;
+ }
+
+ return list;
+}
+
+void StyleEngine::setClass (const char *klass) {
+ DoctreeNode *dn = doctree->top ();
+ assert (dn->klass == NULL);
+ dn->klass = splitStr (klass, ' ');
+};
+
+void StyleEngine::setStyle (const char *style) {
+ Node *n = stack->getRef (stack->size () - 1);
+ assert (n->styleAttribute == NULL);
+ n->styleAttribute = dStrdup (style);
+};
+
+/**
+ * \brief set properties that were definded using (mostly deprecated) HTML
+ * attributes (e.g. bgColor).
+ */
+void StyleEngine::setNonCssHints (CssPropertyList *nonCssHints) {
+ if (stack->getRef (stack->size () - 1)->style)
+ stack->getRef (stack->size () - 1)->style->unref ();
+ style0 (nonCssHints); // evaluate now, so caller can free nonCssHints
+}
+
+/**
+ * \brief Use of the background color of the parent style as default.
+ * This is only used in table code to allow for colors specified for
+ * table rows as table rows are currently no widgets and therefore
+ * don't draw any background.
+ */
+void StyleEngine::inheritBackgroundColor () {
+ stack->getRef (stack->size () - 1)->inheritBackgroundColor = true;
+}
+
+/**
+ * \brief set the CSS pseudo class :link.
+ */
+void StyleEngine::setPseudoLink () {
+ DoctreeNode *dn = doctree->top ();
+ dn->pseudo = "link";
+}
+
+/**
+ * \brief set the CSS pseudo class :visited.
+ */
+void StyleEngine::setPseudoVisited () {
+ DoctreeNode *dn = doctree->top ();
+ dn->pseudo = "visited";
+}
+
+/**
+ * \brief tell the styleEngine that a html element has ended.
+ */
+void StyleEngine::endElement (int element) {
+ assert (stack->size () > 0);
+ assert (element == doctree->top ()->element);
+
+ Node *n = stack->getRef (stack->size () - 1);
+
+ if (n->style)
+ n->style->unref ();
+ if (n->wordStyle)
+ n->wordStyle->unref ();
+ if (n->styleAttribute)
+ dFree ((void*) n->styleAttribute);
+
+ doctree->pop ();
+ stack->setSize (stack->size () - 1);
+}
+
+/**
+ * \brief Make changes to StyleAttrs attrs according to CssPropertyList props.
+ */
+void StyleEngine::apply (StyleAttrs *attrs, CssPropertyList *props) {
+ FontAttrs fontAttrs = *attrs->font;
+ Font *parentFont = stack->get (stack->size () - 2).style->font;
+ char *c, *fontName;
+ int lineHeight;
+
+ /* Determine font first so it can be used to resolve relative lenths. */
+ for (int i = 0; i < props->size (); i++) {
+ CssProperty *p = props->getRef (i);
+
+ switch (p->name) {
+ case CSS_PROPERTY_FONT_FAMILY:
+ // Check font names in comma separated list.
+ // Note, that p->value.strVal is modified, so that in future calls
+ // the matching font name can be used directly.
+ fontName = NULL;
+ while (p->value.strVal) {
+ if ((c = strchr(p->value.strVal, ',')))
+ *c = '\0';
+ dStrstrip(p->value.strVal);
+
+ if (strcmp (p->value.strVal, "serif") == 0)
+ fontName = prefs.font_serif;
+ else if (strcmp (p->value.strVal, "sans-serif") == 0)
+ fontName = prefs.font_sans_serif;
+ else if (strcmp (p->value.strVal, "cursive") == 0)
+ fontName = prefs.font_cursive;
+ else if (strcmp (p->value.strVal, "fantasy") == 0)
+ fontName = prefs.font_fantasy;
+ else if (strcmp (p->value.strVal, "monospace") == 0)
+ fontName = prefs.font_monospace;
+ else if (Font::exists(layout, p->value.strVal))
+ fontName = p->value.strVal;
+
+ if (fontName) { // font found
+ fontAttrs.name = fontName;
+ break;
+ } else if (c) { // try next from list
+ memmove(p->value.strVal, c + 1, strlen(c + 1) + 1);
+ } else { // no font found
+ break;
+ }
+ }
+
+ break;
+ case CSS_PROPERTY_FONT_SIZE:
+ if (p->type == CSS_TYPE_ENUM) {
+ switch (p->value.intVal) {
+ case CSS_FONT_SIZE_XX_SMALL:
+ fontAttrs.size = (int) (11.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_X_SMALL:
+ fontAttrs.size = (int) (12.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_SMALL:
+ fontAttrs.size = (int) (13.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_MEDIUM:
+ fontAttrs.size = (int) (14.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_LARGE:
+ fontAttrs.size = (int) (15.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_X_LARGE:
+ fontAttrs.size = (int) (16.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_XX_LARGE:
+ fontAttrs.size = (int) (17.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_SMALLER:
+ fontAttrs.size -= (int) (1.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_LARGER:
+ fontAttrs.size += (int) (1.0 * prefs.font_factor + 0.5);
+ break;
+ default:
+ assert(false); // invalid font-size enum
+ }
+ } else {
+ computeValue (&fontAttrs.size, p->value.intVal, parentFont,
+ parentFont->size);
+ }
+
+ if (fontAttrs.size < prefs.font_min_size)
+ fontAttrs.size = prefs.font_min_size;
+ if (fontAttrs.size > prefs.font_max_size)
+ fontAttrs.size = prefs.font_max_size;
+
+ break;
+ case CSS_PROPERTY_FONT_STYLE:
+ fontAttrs.style = (FontStyle) p->value.intVal;
+ break;
+ case CSS_PROPERTY_FONT_WEIGHT:
+
+ if (p->type == CSS_TYPE_ENUM) {
+ switch (p->value.intVal) {
+ case CSS_FONT_WEIGHT_BOLD:
+ fontAttrs.weight = 700;
+ break;
+ case CSS_FONT_WEIGHT_BOLDER:
+ fontAttrs.weight += 300;
+ break;
+ case CSS_FONT_WEIGHT_LIGHT:
+ fontAttrs.weight = 100;
+ break;
+ case CSS_FONT_WEIGHT_LIGHTER:
+ fontAttrs.weight -= 300;
+ break;
+ case CSS_FONT_WEIGHT_NORMAL:
+ fontAttrs.weight = 400;
+ break;
+ default:
+ assert(false); // invalid font weight value
+ break;
+ }
+ } else {
+ fontAttrs.weight = p->value.intVal;
+ }
+
+ if (fontAttrs.weight < 100)
+ fontAttrs.weight = 100;
+ if (fontAttrs.weight > 900)
+ fontAttrs.weight = 900;
+
+ break;
+ case CSS_PROPERTY_LETTER_SPACING:
+ if (p->type == CSS_TYPE_ENUM) {
+ if (p->value.intVal == CSS_LETTER_SPACING_NORMAL) {
+ fontAttrs.letterSpacing = 0;
+ }
+ } else {
+ computeValue (&fontAttrs.letterSpacing, p->value.intVal,
+ parentFont, parentFont->size);
+ }
+
+ /* Limit letterSpacing to reasonable values to avoid overflows e.g,
+ * when measuring word width.
+ */
+ if (fontAttrs.letterSpacing > 1000)
+ fontAttrs.letterSpacing = 1000;
+ else if (fontAttrs.letterSpacing < -1000)
+ fontAttrs.letterSpacing = -1000;
+ break;
+ default:
+ break;
+ }
+ }
+
+ attrs->font = Font::create (layout, &fontAttrs);
+
+ for (int i = 0; i < props->size (); i++) {
+ CssProperty *p = props->getRef (i);
+
+ switch (p->name) {
+ /* \todo missing cases */
+ case CSS_PROPERTY_BACKGROUND_COLOR:
+ if (prefs.allow_white_bg || p->value.intVal != 0xffffff)
+ attrs->backgroundColor = Color::create(layout, p->value.intVal);
+ else
+ //attrs->backgroundColor = Color::create(layout, 0xdcd1ba);
+ attrs->backgroundColor = Color::create(layout, 0xe0e0a3);
+ break;
+ case CSS_PROPERTY_BORDER_TOP_COLOR:
+ attrs->borderColor.top =
+ Color::create (layout, p->value.intVal);
+ break;
+ case CSS_PROPERTY_BORDER_BOTTOM_COLOR:
+ attrs->borderColor.bottom =
+ Color::create (layout, p->value.intVal);
+ break;
+ case CSS_PROPERTY_BORDER_LEFT_COLOR:
+ attrs->borderColor.left =
+ Color::create (layout, p->value.intVal);
+ break;
+ case CSS_PROPERTY_BORDER_RIGHT_COLOR:
+ attrs->borderColor.right =
+ Color::create (layout, p->value.intVal);
+ break;
+ case CSS_PROPERTY_BORDER_BOTTOM_STYLE:
+ attrs->borderStyle.bottom = (BorderStyle) p->value.intVal;
+ break;
+ case CSS_PROPERTY_BORDER_LEFT_STYLE:
+ attrs->borderStyle.left = (BorderStyle) p->value.intVal;
+ break;
+ case CSS_PROPERTY_BORDER_RIGHT_STYLE:
+ attrs->borderStyle.right = (BorderStyle) p->value.intVal;
+ break;
+ case CSS_PROPERTY_BORDER_TOP_STYLE:
+ attrs->borderStyle.top = (BorderStyle) p->value.intVal;
+ break;
+ case CSS_PROPERTY_BORDER_BOTTOM_WIDTH:
+ computeBorderWidth (&attrs->borderWidth.bottom, p, attrs->font);
+ break;
+ case CSS_PROPERTY_BORDER_LEFT_WIDTH:
+ computeBorderWidth (&attrs->borderWidth.left, p, attrs->font);
+ break;
+ case CSS_PROPERTY_BORDER_RIGHT_WIDTH:
+ computeBorderWidth (&attrs->borderWidth.right, p, attrs->font);
+ break;
+ case CSS_PROPERTY_BORDER_TOP_WIDTH:
+ computeBorderWidth (&attrs->borderWidth.top, p, attrs->font);
+ break;
+ case CSS_PROPERTY_BORDER_SPACING:
+ computeValue (&attrs->hBorderSpacing, p->value.intVal,attrs->font);
+ computeValue (&attrs->vBorderSpacing, p->value.intVal,attrs->font);
+ break;
+ case CSS_PROPERTY_COLOR:
+ attrs->color = Color::create (layout, p->value.intVal);
+ break;
+ case CSS_PROPERTY_CURSOR:
+ attrs->cursor = (Cursor) p->value.intVal;
+ break;
+ case CSS_PROPERTY_DISPLAY:
+ attrs->display = (DisplayType) p->value.intVal;
+ break;
+ case CSS_PROPERTY_LINE_HEIGHT:
+ if (p->type == CSS_TYPE_ENUM) { //only valid enum value is "normal"
+ attrs->lineHeight = dw::core::style::LENGTH_AUTO;
+ } else if (p->type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER) {
+ if (CSS_LENGTH_TYPE (p->value.intVal) == CSS_LENGTH_TYPE_NONE) {
+ attrs->lineHeight =
+ createPerLength(CSS_LENGTH_VALUE(p->value.intVal));
+ } else {
+ computeValue (&lineHeight, p->value.intVal, attrs->font,
+ attrs->font->size);
+ attrs->lineHeight = createAbsLength(lineHeight);
+ }
+ }
+ break;
+ case CSS_PROPERTY_LIST_STYLE_POSITION:
+ attrs->listStylePosition = (ListStylePosition) p->value.intVal;
+ break;
+ case CSS_PROPERTY_LIST_STYLE_TYPE:
+ attrs->listStyleType = (ListStyleType) p->value.intVal;
+ break;
+ case CSS_PROPERTY_MARGIN_BOTTOM:
+ computeValue (&attrs->margin.bottom, p->value.intVal, attrs->font);
+ if (attrs->margin.bottom < 0) // \todo fix negative margins in dw/*
+ attrs->margin.bottom = 0;
+ break;
+ case CSS_PROPERTY_MARGIN_LEFT:
+ computeValue (&attrs->margin.left, p->value.intVal, attrs->font);
+ if (attrs->margin.left < 0) // \todo fix negative margins in dw/*
+ attrs->margin.left = 0;
+ break;
+ case CSS_PROPERTY_MARGIN_RIGHT:
+ computeValue (&attrs->margin.right, p->value.intVal, attrs->font);
+ if (attrs->margin.right < 0) // \todo fix negative margins in dw/*
+ attrs->margin.right = 0;
+ break;
+ case CSS_PROPERTY_MARGIN_TOP:
+ computeValue (&attrs->margin.top, p->value.intVal, attrs->font);
+ if (attrs->margin.top < 0) // \todo fix negative margins in dw/*
+ attrs->margin.top = 0;
+ break;
+ case CSS_PROPERTY_PADDING_TOP:
+ computeValue (&attrs->padding.top, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_PADDING_BOTTOM:
+ computeValue (&attrs->padding.bottom, p->value.intVal,attrs->font);
+ break;
+ case CSS_PROPERTY_PADDING_LEFT:
+ computeValue (&attrs->padding.left, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_PADDING_RIGHT:
+ computeValue (&attrs->padding.right, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_TEXT_ALIGN:
+ attrs->textAlign = (TextAlignType) p->value.intVal;
+ break;
+ case CSS_PROPERTY_TEXT_DECORATION:
+ attrs->textDecoration |= p->value.intVal;
+ break;
+ case CSS_PROPERTY_VERTICAL_ALIGN:
+ attrs->valign = (VAlignType) p->value.intVal;
+ break;
+ case CSS_PROPERTY_WHITE_SPACE:
+ attrs->whiteSpace = (WhiteSpace) p->value.intVal;
+ break;
+ case CSS_PROPERTY_WIDTH:
+ computeLength (&attrs->width, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_HEIGHT:
+ computeLength (&attrs->height, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_WORD_SPACING:
+ if (p->type == CSS_TYPE_ENUM) {
+ if (p->value.intVal == CSS_WORD_SPACING_NORMAL) {
+ attrs->wordSpacing = 0;
+ }
+ } else {
+ computeValue(&attrs->wordSpacing, p->value.intVal, attrs->font);
+ }
+
+ /* Limit to reasonable values to avoid overflows */
+ if (attrs->wordSpacing > 1000)
+ attrs->wordSpacing = 1000;
+ else if (attrs->wordSpacing < -1000)
+ attrs->wordSpacing = -1000;
+ break;
+ case PROPERTY_X_LINK:
+ attrs->x_link = p->value.intVal;
+ break;
+ case PROPERTY_X_IMG:
+ attrs->x_img = p->value.intVal;
+ break;
+ case PROPERTY_X_TOOLTIP:
+ attrs->x_tooltip = Tooltip::create(layout, p->value.strVal);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* make sure border colors are set */
+ if (attrs->borderColor.top == NULL)
+ attrs->borderColor.top = attrs->color;
+ if (attrs->borderColor.bottom == NULL)
+ attrs->borderColor.bottom = attrs->color;
+ if (attrs->borderColor.left == NULL)
+ attrs->borderColor.left = attrs->color;
+ if (attrs->borderColor.right == NULL)
+ attrs->borderColor.right = attrs->color;
+
+}
+
+/**
+ * \brief Resolve relative lengths to absolute values.
+ */
+bool StyleEngine::computeValue (int *dest, CssLength value, Font *font) {
+ static float dpmm;
+
+ if (dpmm == 0.0)
+ dpmm = layout->dpiX () / 25.4; /* assume dpiX == dpiY */
+
+ switch (CSS_LENGTH_TYPE (value)) {
+ case CSS_LENGTH_TYPE_PX:
+ *dest = (int) CSS_LENGTH_VALUE (value);
+ return true;
+ case CSS_LENGTH_TYPE_MM:
+ *dest = (int) (CSS_LENGTH_VALUE (value) * dpmm + 0.5);
+ return true;
+ case CSS_LENGTH_TYPE_EM:
+ *dest = (int) (CSS_LENGTH_VALUE (value) * font->size + 0.5);
+ return true;
+ case CSS_LENGTH_TYPE_EX:
+ *dest = (int) (CSS_LENGTH_VALUE(value) * font->xHeight + 0.5);
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool StyleEngine::computeValue (int *dest, CssLength value, Font *font,
+ int percentageBase) {
+ if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_PERCENTAGE) {
+ *dest = (int) (CSS_LENGTH_VALUE (value) * percentageBase + 0.5);
+ return true;
+ } else
+ return computeValue (dest, value, font);
+}
+
+bool StyleEngine::computeLength (dw::core::style::Length *dest,
+ CssLength value, Font *font) {
+ int v;
+
+ if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_PERCENTAGE) {
+ *dest = createPerLength (CSS_LENGTH_VALUE (value));
+ return true;
+ } else if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_AUTO) {
+ *dest = dw::core::style::LENGTH_AUTO;
+ return true;
+ } else if (computeValue (&v, value, font)) {
+ *dest = createAbsLength (v);
+ return true;
+ }
+
+ return false;
+}
+
+void StyleEngine::computeBorderWidth (int *dest, CssProperty *p,
+ dw::core::style::Font *font) {
+ if (p->type == CSS_TYPE_ENUM) {
+ switch (p->value.intVal) {
+ case CSS_BORDER_WIDTH_THIN:
+ *dest = 1;
+ break;
+ case CSS_BORDER_WIDTH_MEDIUM:
+ *dest = 2;
+ break;
+ case CSS_BORDER_WIDTH_THICK:
+ *dest = 3;
+ break;
+ default:
+ assert(false);
+ }
+ } else {
+ computeValue (dest, p->value.intVal, font);
+ }
+}
+
+/**
+ * \brief Similar to StyleEngine::style(), but with backgroundColor set.
+ * A normal style might have backgroundColor == NULL to indicate a transparent
+ * background. This method ensures that backgroundColor is set.
+ */
+Style * StyleEngine::backgroundStyle () {
+ StyleAttrs attrs = *style ();
+
+ for (int i = stack->size () - 1; i >= 0 && ! attrs.backgroundColor; i--)
+ attrs.backgroundColor = stack->getRef (i)->style->backgroundColor;
+
+ assert (attrs.backgroundColor);
+ return Style::create (layout, &attrs);
+}
+
+/**
+ * \brief Create a new style object based on the previously opened / closed
+ * HTML elements and the nonCssProperties that have been set.
+ * This method is private. Call style() to get a current style object.
+ */
+Style * StyleEngine::style0 (CssPropertyList *nonCssProperties) {
+ CssPropertyList props, *styleAttributeProps = NULL;
+ const char *styleAttribute =
+ stack->getRef (stack->size () - 1)->styleAttribute;
+ // get previous style from the stack
+ StyleAttrs attrs = *stack->getRef (stack->size () - 2)->style;
+
+ // Ensure that StyleEngine::style0() has not been called before for
+ // this element.
+ // Style computation is expensive so limit it as much as possible.
+ // If this assertion is hit, you need to rearrange the code that is
+ // doing styleEngine calls to call setNonCssHints() before calling
+ // style() or wordStyle() for each new element.
+ assert (stack->getRef (stack->size () - 1)->style == NULL);
+
+ // reset values that are not inherited according to CSS
+ attrs.resetValues ();
+
+ if (stack->getRef (stack->size () - 2)->inheritBackgroundColor) {
+ attrs.backgroundColor =
+ stack->getRef (stack->size () - 2)->style->backgroundColor;
+
+ attrs.valign = stack->getRef (stack->size () - 2)->style->valign;
+ }
+
+ // parse style information from style="" attribute, if it exists
+ if (styleAttribute && prefs.parse_embedded_css)
+ styleAttributeProps =
+ CssParser::parseDeclarationBlock (styleAttribute,
+ strlen (styleAttribute));
+
+ // merge style information
+ cssContext->apply (&props, doctree, styleAttributeProps, nonCssProperties);
+
+ // apply style
+ apply (&attrs, &props);
+
+ stack->getRef (stack->size () - 1)->style = Style::create (layout, &attrs);
+
+ if (styleAttributeProps)
+ delete styleAttributeProps;
+
+ return stack->getRef (stack->size () - 1)->style;
+}
+
+Style * StyleEngine::wordStyle0 (CssPropertyList *nonCssProperties) {
+ StyleAttrs attrs = *style ();
+ attrs.resetValues ();
+
+ if (stack->getRef (stack->size () - 1)->inheritBackgroundColor)
+ attrs.backgroundColor = style ()->backgroundColor;
+
+ attrs.valign = style ()->valign;
+
+ stack->getRef(stack->size() - 1)->wordStyle = Style::create(layout, &attrs);
+ return stack->getRef (stack->size () - 1)->wordStyle;
+}
+
+void StyleEngine::parse (DilloHtml *html, DilloUrl *url, const char *buf,
+ int buflen, CssOrigin origin) {
+ if (importDepth > 10) { // avoid looping with recursive @import directives
+ MSG_WARN("Maximum depth of CSS @import reached--ignoring stylesheet.\n");
+ return;
+ }
+
+ importDepth++;
+ CssParser::parse (html, url, cssContext, buf, buflen, origin);
+ importDepth--;
+}
diff --git a/src/styleengine.hh b/src/styleengine.hh
new file mode 100644
index 00000000..66f28cee
--- /dev/null
+++ b/src/styleengine.hh
@@ -0,0 +1,84 @@
+#ifndef __STYLEENGINE_HH__
+#define __STYLEENGINE_HH__
+
+class StyleEngine;
+
+#include "dw/core.hh"
+#include "doctree.hh"
+#include "css.hh"
+#include "cssparser.hh"
+
+/**
+ * \brief This class provides the glue between HTML parser and CSS subsystem.
+ *
+ * It maintains a document tree and creates and caches style objects for use
+ * by the HTML parser.
+ * The HTML parser in turn informs StyleEngine about opened or closed
+ * HTML elements and their attributes via the startElement() / endElement()
+ * methods.
+ */
+class StyleEngine {
+ private:
+ class Node {
+ public:
+ dw::core::style::Style *style;
+ dw::core::style::Style *wordStyle;
+ const char *styleAttribute;
+ bool inheritBackgroundColor;
+ };
+
+ dw::core::Layout *layout;
+ lout::misc::SimpleVector <Node> *stack;
+ CssContext *cssContext;
+ Doctree *doctree;
+ int importDepth;
+
+ dw::core::style::Style *style0 (CssPropertyList *nonCssHints = NULL);
+ dw::core::style::Style *wordStyle0 (CssPropertyList *nonCssHints = NULL);
+ void apply (dw::core::style::StyleAttrs *attrs, CssPropertyList *props);
+ bool computeValue (int *dest, CssLength value,
+ dw::core::style::Font *font);
+ bool computeValue (int *dest, CssLength value,
+ dw::core::style::Font *font, int percentageBase);
+ bool computeLength (dw::core::style::Length *dest, CssLength value,
+ dw::core::style::Font *font);
+ void computeBorderWidth (int *dest, CssProperty *p,
+ dw::core::style::Font *font);
+
+ public:
+ StyleEngine (dw::core::Layout *layout);
+ ~StyleEngine ();
+
+ void parse (DilloHtml *html, DilloUrl *url, const char *buf, int buflen,
+ CssOrigin origin);
+ void startElement (int tag);
+ void startElement (const char *tagname);
+ void setId (const char *id);
+ const char * getId () { return doctree->top ()->id; };
+ void setClass (const char *klass);
+ void setStyle (const char *style);
+ void endElement (int tag);
+ void setPseudoLink ();
+ void setPseudoVisited ();
+ void setNonCssHints (CssPropertyList *nonCssHints);
+ void inheritBackgroundColor (); /* \todo get rid of this somehow */
+ dw::core::style::Style *backgroundStyle ();
+
+ inline dw::core::style::Style *style () {
+ dw::core::style::Style *s = stack->getRef (stack->size () - 1)->style;
+ if (s)
+ return s;
+ else
+ return style0 ();
+ };
+
+ inline dw::core::style::Style *wordStyle () {
+ dw::core::style::Style *s = stack->getRef(stack->size()-1)->wordStyle;
+ if (s)
+ return s;
+ else
+ return wordStyle0 ();
+ };
+};
+
+#endif
diff --git a/src/table.cc b/src/table.cc
index 589f7e1e..43304206 100644
--- a/src/table.cc
+++ b/src/table.cc
@@ -18,40 +18,31 @@
#include "prefs.h"
#include "msg.h"
-
-/* Undefine if you want to unroll tables. For instance for PDAs */
-#define USE_TABLES
-
-#define dillo_dbg_rendering 0
+#include "css.hh"
using namespace dw;
using namespace dw::core;
using namespace dw::core::style;
/*
- * Forward declarations
+ * Forward declarations
*/
static void Html_tag_open_table_cell(DilloHtml *html,
const char *tag, int tagsize,
- dw::core::style::TextAlignType text_align);
+ dw::core::style::TextAlignType text_align);
/*
* <TABLE>
*/
void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
{
-#ifdef USE_TABLES
dw::core::Widget *table;
- dw::core::style::StyleAttrs style_attrs;
- dw::core::style::Style *cell_style, *old_style;
+ CssPropertyList props, *table_cell_props;
const char *attrbuf;
- int32_t border = 0, cellspacing = 1, cellpadding = 2, bgcolor;
-#endif
+ int32_t border = -1, cellspacing = -1, cellpadding = -1, bgcolor = -1;
+ CssLength cssLength;
- DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style);
-
-#ifdef USE_TABLES
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border")))
border = isdigit(attrbuf[0]) ? strtol (attrbuf, NULL, 10) : 1;
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellspacing")))
@@ -59,73 +50,85 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellpadding")))
cellpadding = strtol (attrbuf, NULL, 10);
- /* The style for the table */
- style_attrs = *S_TOP(html)->style;
-
- /* When dillo was started with the --debug-rendering option, there
- * is always a border around the table. */
- if (dillo_dbg_rendering)
- style_attrs.borderWidth.setVal (MIN (border, 1));
- else
- style_attrs.borderWidth.setVal (border);
+ if (border != -1) {
+ cssLength = CSS_CREATE_LENGTH (border, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_BORDER_TOP_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ cssLength);
+ props.set (CSS_PROPERTY_BORDER_BOTTOM_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ cssLength);
+ props.set (CSS_PROPERTY_BORDER_LEFT_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ cssLength);
+ props.set (CSS_PROPERTY_BORDER_RIGHT_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ cssLength);
+ }
- style_attrs.setBorderColor (
- Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color));
- style_attrs.setBorderStyle (BORDER_OUTSET);
- style_attrs.hBorderSpacing = cellspacing;
- style_attrs.vBorderSpacing = cellspacing;
+ if (cellspacing != -1) {
+ cssLength = CSS_CREATE_LENGTH (cellspacing, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_BORDER_SPACING, CSS_TYPE_LENGTH_PERCENTAGE,
+ cssLength);
+ }
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "width")))
- style_attrs.width = a_Html_parse_length (html, attrbuf);
+ props.set (CSS_PROPERTY_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ a_Html_parse_length (html, attrbuf));
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) {
if (dStrcasecmp (attrbuf, "left") == 0)
- style_attrs.textAlign = dw::core::style::TEXT_ALIGN_LEFT;
+ props.set (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_LEFT);
else if (dStrcasecmp (attrbuf, "right") == 0)
- style_attrs.textAlign = dw::core::style::TEXT_ALIGN_RIGHT;
+ props.set (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_RIGHT);
else if (dStrcasecmp (attrbuf, "center") == 0)
- style_attrs.textAlign = dw::core::style::TEXT_ALIGN_CENTER;
+ props.set (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_CENTER);
}
- if (!prefs.force_my_colors &&
- (attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
bgcolor = a_Html_color_parse(html, attrbuf, -1);
- if (bgcolor != -1) {
- if (bgcolor == 0xffffff && !prefs.allow_white_bg)
- bgcolor = prefs.bg_color;
- S_TOP(html)->current_bg_color = bgcolor;
- style_attrs.backgroundColor =
- Color::createShaded (HT2LT(html), bgcolor);
- }
+ if (bgcolor != -1)
+ props.set (CSS_PROPERTY_BACKGROUND_COLOR, CSS_TYPE_COLOR, bgcolor);
}
+ html->styleEngine->setNonCssHints (&props);
+
+ HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
+
/* The style for the cells */
- cell_style = Style::create (HT2LT(html), &style_attrs);
- style_attrs = *S_TOP(html)->style;
- /* When dillo was started with the --debug-rendering option, there
- * is always a border around the cells. */
- if (dillo_dbg_rendering)
- style_attrs.borderWidth.setVal (1);
- else
- style_attrs.borderWidth.setVal (border ? 1 : 0);
- style_attrs.padding.setVal(cellpadding);
- style_attrs.setBorderColor (cell_style->borderColor.top);
- style_attrs.setBorderStyle (BORDER_INSET);
-
- old_style = S_TOP(html)->table_cell_style;
- S_TOP(html)->table_cell_style =
- Style::create (HT2LT(html), &style_attrs);
- if (old_style)
- old_style->unref ();
+ table_cell_props = new CssPropertyList ();
+ if (border > 0) {
+ cssLength = CSS_CREATE_LENGTH (1, CSS_LENGTH_TYPE_PX);
+ table_cell_props->set (CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ }
+
+ if (cellpadding != -1) {
+ cssLength = CSS_CREATE_LENGTH (cellpadding, CSS_LENGTH_TYPE_PX);
+ table_cell_props->set (CSS_PROPERTY_PADDING_TOP,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_PADDING_BOTTOM,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_PADDING_LEFT,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_PADDING_RIGHT,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ }
+
+ if (S_TOP(html)->table_cell_props)
+ S_TOP(html)->table_cell_props->unref ();
+
+ S_TOP(html)->table_cell_props = table_cell_props;
+ S_TOP(html)->table_cell_props->ref ();
table = new dw::Table(prefs.limit_text_width);
- DW2TB(html->dw)->addWidget (table, cell_style);
- cell_style->unref ();
+ HT2TB(html)->addWidget (table, html->styleEngine->style ());
S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TOP;
S_TOP(html)->cell_text_align_set = FALSE;
S_TOP(html)->table = table;
-#endif
}
/*
@@ -134,12 +137,10 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize)
{
const char *attrbuf;
- dw::core::style::StyleAttrs style_attrs;
- dw::core::style::Style *style, *old_style;
int32_t bgcolor = -1;
bool new_style = false;
+ CssPropertyList props, *table_cell_props;
-#ifdef USE_TABLES
switch (S_TOP(html)->table_mode) {
case DILLO_HTML_TABLE_MODE_NONE:
_MSG("Invalid HTML syntax: <tr> outside <table>\n");
@@ -148,44 +149,37 @@ void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize)
case DILLO_HTML_TABLE_MODE_TOP:
case DILLO_HTML_TABLE_MODE_TR:
case DILLO_HTML_TABLE_MODE_TD:
- style = NULL;
- if (!prefs.force_my_colors &&
- (attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
bgcolor = a_Html_color_parse(html, attrbuf, -1);
- if (bgcolor != -1) {
- if (bgcolor == 0xffffff && !prefs.allow_white_bg)
- bgcolor = prefs.bg_color;
-
- style_attrs = *S_TOP(html)->style;
- style_attrs.backgroundColor =
- Color::createShaded (HT2LT(html), bgcolor);
- style = Style::create (HT2LT(html), &style_attrs);
- S_TOP(html)->current_bg_color = bgcolor;
- }
+ if (bgcolor != -1)
+ props.set (CSS_PROPERTY_BACKGROUND_COLOR, CSS_TYPE_COLOR, bgcolor);
}
- ((dw::Table*)S_TOP(html)->table)->addRow (style);
- if (style)
- style->unref ();
-
if (a_Html_get_attr (html, tag, tagsize, "align")) {
S_TOP(html)->cell_text_align_set = TRUE;
- a_Html_tag_set_align_attr (html, tag, tagsize);
+ a_Html_tag_set_align_attr (html, &props, tag, tagsize);
}
- style_attrs = *S_TOP(html)->table_cell_style;
+ html->styleEngine->inheritBackgroundColor ();
+ html->styleEngine->setNonCssHints (&props);
+
+ ((dw::Table*)S_TOP(html)->table)->addRow (html->styleEngine->style ());
+
+ table_cell_props = new CssPropertyList (*S_TOP(html)->table_cell_props);
if (bgcolor != -1) {
- style_attrs.backgroundColor =Color::createShaded(HT2LT(html),bgcolor);
+ table_cell_props->set (CSS_PROPERTY_BACKGROUND_COLOR,
+ CSS_TYPE_COLOR, bgcolor);
new_style = true;
}
- if (a_Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs))
+ if (a_Html_tag_set_valign_attr (html, tag, tagsize, table_cell_props))
new_style = true;
if (new_style) {
- old_style = S_TOP(html)->table_cell_style;
- S_TOP(html)->table_cell_style =
- Style::create (HT2LT(html), &style_attrs);
- old_style->unref ();
+ S_TOP(html)->table_cell_props->unref ();
+ S_TOP(html)->table_cell_props = table_cell_props;
+ S_TOP(html)->table_cell_props->ref ();
+ } else {
+ delete table_cell_props;
}
break;
default:
@@ -193,9 +187,6 @@ void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize)
}
S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TR;
-#else
- DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style);
-#endif
}
/*
@@ -212,13 +203,12 @@ void Html_tag_open_td(DilloHtml *html, const char *tag, int tagsize)
*/
void Html_tag_open_th(DilloHtml *html, const char *tag, int tagsize)
{
- a_Html_set_top_font(html, NULL, 0, 1, 1);
Html_tag_open_table_cell (html, tag, tagsize,
dw::core::style::TEXT_ALIGN_CENTER);
}
/*
- * Utilities
+ * Utilities
*/
/*
@@ -228,12 +218,9 @@ static void Html_tag_open_table_cell(DilloHtml *html,
const char *tag, int tagsize,
dw::core::style::TextAlignType text_align)
{
-#ifdef USE_TABLES
Widget *col_tb;
int colspan = 1, rowspan = 1;
const char *attrbuf;
- dw::core::style::StyleAttrs style_attrs;
- dw::core::style::Style *style, *old_style;
int32_t bgcolor;
bool_t new_style;
@@ -258,66 +245,53 @@ static void Html_tag_open_table_cell(DilloHtml *html,
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rowspan")))
rowspan = MAX(1, strtol (attrbuf, NULL, 10));
+ CssPropertyList *props;
+ // \todo any shorter way to do this?
+ if (S_TOP(html)->table_cell_props != NULL)
+ props = new CssPropertyList (*S_TOP(html)->table_cell_props);
+ else
+ props = new CssPropertyList ();
+
/* text style */
- old_style = S_TOP(html)->style;
- style_attrs = *old_style;
- if (!S_TOP(html)->cell_text_align_set)
- style_attrs.textAlign = text_align;
+ if (!S_TOP(html)->cell_text_align_set) {
+ props->set (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, text_align);
+ }
if (a_Html_get_attr(html, tag, tagsize, "nowrap"))
- style_attrs.whiteSpace = WHITE_SPACE_NOWRAP;
+ props->set(CSS_PROPERTY_WHITE_SPACE,CSS_TYPE_ENUM,WHITE_SPACE_NOWRAP);
else
- style_attrs.whiteSpace = WHITE_SPACE_NORMAL;
-
- S_TOP(html)->style =
- Style::create (HT2LT(html), &style_attrs);
- old_style->unref ();
- a_Html_tag_set_align_attr (html, tag, tagsize);
+ props->set(CSS_PROPERTY_WHITE_SPACE,CSS_TYPE_ENUM,WHITE_SPACE_NORMAL);
- /* cell style */
- style_attrs = *S_TOP(html)->table_cell_style;
- new_style = FALSE;
+ a_Html_tag_set_align_attr (html, props, tag, tagsize);
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "width"))) {
- style_attrs.width = a_Html_parse_length (html, attrbuf);
- new_style = TRUE;
+ props->set (CSS_PROPERTY_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ a_Html_parse_length (html, attrbuf));
}
- if (a_Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs))
+ if (a_Html_tag_set_valign_attr (html, tag, tagsize, props))
new_style = TRUE;
- if (!prefs.force_my_colors &&
- (attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
bgcolor = a_Html_color_parse(html, attrbuf, -1);
- if (bgcolor != -1) {
- if (bgcolor == 0xffffff && !prefs.allow_white_bg)
- bgcolor = prefs.bg_color;
-
- new_style = TRUE;
- style_attrs.backgroundColor =
- Color::createShaded (HT2LT(html), bgcolor);
- S_TOP(html)->current_bg_color = bgcolor;
- }
+ if (bgcolor != -1)
+ props->set (CSS_PROPERTY_BACKGROUND_COLOR, CSS_TYPE_COLOR,bgcolor);
}
- if (S_TOP(html)->style->textAlign
+ html->styleEngine->setNonCssHints (props);
+ delete props;
+
+ if (html->styleEngine->style ()->textAlign
== TEXT_ALIGN_STRING)
- col_tb = new dw::TableCell (((dw::Table*)S_TOP(html)->table)->getCellRef (),
- prefs.limit_text_width);
+ col_tb = new dw::TableCell (
+ ((dw::Table*)S_TOP(html)->table)->getCellRef (),
+ prefs.limit_text_width);
else
col_tb = new Textblock (prefs.limit_text_width);
- if (new_style) {
- style = dw::core::style::Style::create (HT2LT(html), &style_attrs);
- col_tb->setStyle (style);
- style->unref ();
- } else
- col_tb->setStyle (S_TOP(html)->table_cell_style);
+ col_tb->setStyle (html->styleEngine->style ());
((dw::Table*)S_TOP(html)->table)->addCell (col_tb, colspan, rowspan);
S_TOP(html)->textblock = html->dw = col_tb;
-
- /* Handle it when the user clicks on a link */
- html->connectSignals(col_tb);
break;
default:
@@ -326,5 +300,4 @@ static void Html_tag_open_table_cell(DilloHtml *html,
}
S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TD;
-#endif
}
diff --git a/src/table.hh b/src/table.hh
index 8068c184..aca717ba 100644
--- a/src/table.hh
+++ b/src/table.hh
@@ -2,13 +2,13 @@
#define __TABLE_HH__
/*
- * Classes
+ * Classes
*/
class DilloHtml;
/*
- * Table parsing functions
+ * Table parsing functions
*/
void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize);
diff --git a/src/timeout.cc b/src/timeout.cc
index 356134cc..80eb6425 100644
--- a/src/timeout.cc
+++ b/src/timeout.cc
@@ -29,7 +29,7 @@ void a_Timeout_add(float t, TimeoutCb_t cb, void *cbdata)
}
/*
- * To be called from iside the 'cb' function when it wants to keep running
+ * To be called from inside the 'cb' function when it wants to keep running
*/
void a_Timeout_repeat(float t, TimeoutCb_t cb, void *cbdata)
{
diff --git a/src/ui.cc b/src/ui.cc
index 87fab74f..fac78604 100644
--- a/src/ui.cc
+++ b/src/ui.cc
@@ -11,7 +11,7 @@
// UI for Dillo
-#include <stdlib.h>
+#include <unistd.h>
#include <stdio.h>
#include <fltk/HighlightButton.h>
@@ -25,9 +25,11 @@
#include <fltk/Item.h>
#include <fltk/Divider.h>
+#include "keys.hh"
#include "ui.hh"
#include "msg.h"
#include "timeout.hh"
+#include "utf8.hh"
using namespace fltk;
@@ -38,8 +40,9 @@ using namespace fltk;
struct iconset {
Image *ImgMeterOK, *ImgMeterBug,
- *ImgHome, *ImgReload, *ImgSave, *ImgBook, *ImgClear, *ImgSearch;
- MultiImage *ImgLeftMulti, *ImgRightMulti, *ImgStopMulti, *ImgImageLoadMulti;
+ *ImgHome, *ImgReload, *ImgSave, *ImgBook, *ImgTools,
+ *ImgClear,*ImgSearch, *ImgHelp;
+ MultiImage *ImgLeftMulti, *ImgRightMulti, *ImgStopMulti;
};
static struct iconset standard_icons = {
@@ -49,16 +52,16 @@ static struct iconset standard_icons = {
new xpmImage(reload_xpm),
new xpmImage(save_xpm),
new xpmImage(bm_xpm),
+ new xpmImage(tools_xpm),
new xpmImage(new_s_xpm),
new xpmImage(search_xpm),
+ new xpmImage(help_xpm),
new MultiImage(*new xpmImage(left_xpm), INACTIVE_R,
*new xpmImage(left_i_xpm)),
new MultiImage(*new xpmImage(right_xpm), INACTIVE_R,
*new xpmImage(right_i_xpm)),
new MultiImage(*new xpmImage(stop_xpm), INACTIVE_R,
*new xpmImage(stop_i_xpm)),
- new MultiImage(*new xpmImage(imgload_off_xpm), STATE,
- *new xpmImage(imgload_on_xpm))
};
static struct iconset small_icons = {
@@ -68,15 +71,16 @@ static struct iconset small_icons = {
new xpmImage(reload_s_xpm),
new xpmImage(save_s_xpm),
new xpmImage(bm_s_xpm),
+ new xpmImage(tools_s_xpm),
new xpmImage(new_s_xpm),
standard_icons.ImgSearch,
+ standard_icons.ImgHelp,
new MultiImage(*new xpmImage(left_s_xpm), INACTIVE_R,
*new xpmImage(left_si_xpm)),
new MultiImage(*new xpmImage(right_s_xpm), INACTIVE_R,
*new xpmImage(right_si_xpm)),
new MultiImage(*new xpmImage(stop_s_xpm), INACTIVE_R,
*new xpmImage(stop_si_xpm)),
- standard_icons.ImgImageLoadMulti
};
@@ -99,7 +103,7 @@ public:
};
/*
- * Disable: UpKey, DownKey, PageUpKey, PageDownKey and
+ * Disable: UpKey, DownKey, PageUpKey, PageDownKey and
* CTRL+{o,r,HomeKey,EndKey}
*/
int CustInput::handle(int e)
@@ -167,11 +171,11 @@ int CustHighlightButton::handle(int e)
* Used to resize the progress boxes automatically.
*/
class CustProgressBox : public InvisibleBox {
+ int padding;
public:
CustProgressBox(int x, int y, int w, int h, const char *l=0) :
- InvisibleBox(x,y,w,h,l) {};
+ InvisibleBox(x,y,w,h,l) { padding = 0; };
void update_label(const char *lbl) {
- static int padding = 0;
int w,h;
if (!padding) {
copy_label("W");
@@ -189,7 +193,7 @@ public:
// Toolbar buttons -----------------------------------------------------------
//
//static const char *button_names[] = {
-// "Back", "Forward", "Home", "Reload", "Save", "Stop", "Bookmarks",
+// "Back", "Forward", "Home", "Reload", "Save", "Stop", "Bookmarks", "Tools",
// "Clear", "Search"
//};
@@ -215,6 +219,26 @@ static void search_cb(Widget *wid, void *data)
}
/*
+ * Callback for the help button.
+ */
+static void help_cb(Widget *w, void *)
+{
+ char *path = dStrconcat(DILLO_DOCDIR, "user_help.html", NULL);
+ BrowserWindow *bw = a_UIcmd_get_bw_by_widget(w);
+
+ if (access(path, R_OK) == 0) {
+ char *urlstr = dStrconcat("file:", path, NULL);
+ a_UIcmd_open_urlstr(bw, urlstr);
+ dFree(urlstr);
+ } else {
+ MSG("Can't read local help file at \"%s\"."
+ " Getting remote help...\n", path);
+ a_UIcmd_open_urlstr(bw, "http://www.dillo.org/dillo2-help.html");
+ }
+ dFree(path);
+}
+
+/*
* Callback for the File menu button.
*/
static void filemenu_cb(Widget *wid, void *)
@@ -324,6 +348,11 @@ static void b1_cb(Widget *wid, void *cb_data)
a_UIcmd_book(a_UIcmd_get_bw_by_widget(wid));
}
break;
+ case UI_TOOLS:
+ if (k == 1 || k == 3) {
+ a_UIcmd_tools(a_UIcmd_get_bw_by_widget(wid), wid);
+ }
+ break;
default:
break;
}
@@ -370,50 +399,58 @@ PackedGroup *UI::make_toolbar(int tw, int th)
p1->begin();
Back = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Back" : 0);
b->image(icons->ImgLeftMulti);
- b->tooltip("Previous page");
b->callback(b1_cb, (void *)UI_BACK);
b->clear_tab_to_focus();
HighlightButton::default_style->highlight_color(CuteColor);
Forw = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Forw" : 0);
b->image(icons->ImgRightMulti);
- b->tooltip("Next page");
b->callback(b1_cb, (void *)UI_FORW);
b->clear_tab_to_focus();
-
+
Home = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Home" : 0);
b->image(icons->ImgHome);
- b->tooltip("Go to the Home page");
b->callback(b1_cb, (void *)UI_HOME);
b->clear_tab_to_focus();
Reload = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Reload" : 0);
b->image(icons->ImgReload);
- b->tooltip("Reload");
b->callback(b1_cb, (void *)UI_RELOAD);
b->clear_tab_to_focus();
-
+
Save = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Save" : 0);
b->image(icons->ImgSave);
- b->tooltip("Save this page");
b->callback(b1_cb, (void *)UI_SAVE);
b->clear_tab_to_focus();
-
+
Stop = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Stop" : 0);
b->image(icons->ImgStopMulti);
- b->tooltip("Stop loading");
b->callback(b1_cb, (void *)UI_STOP);
b->clear_tab_to_focus();
Bookmarks = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Book" : 0);
b->image(icons->ImgBook);
- b->tooltip("View bookmarks");
b->callback(b1_cb, (void *)UI_BOOK);
b->clear_tab_to_focus();
+ Tools = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Tools" : 0);
+ b->image(icons->ImgTools);
+ b->callback(b1_cb, (void *)UI_TOOLS);
+ b->clear_tab_to_focus();
+
p1->type(PackedGroup::ALL_CHILDREN_VERTICAL);
p1->end();
+ if (prefs.show_tooltip) {
+ Back->tooltip("Previous page");
+ Forw->tooltip("Next page");
+ Home->tooltip("Go to the Home page");
+ Reload->tooltip("Reload");
+ Save->tooltip("Save this page");
+ Stop->tooltip("Stop loading");
+ Bookmarks->tooltip("View bookmarks");
+ Tools->tooltip("Settings");
+ }
return p1;
}
@@ -427,12 +464,10 @@ PackedGroup *UI::make_location()
pg->begin();
Clear = b = new CustHighlightButton(2,2,16,22,0);
b->image(icons->ImgClear);
- b->tooltip("Clear the URL box.\nMiddle-click to paste a URL.");
b->callback(clear_cb, this);
b->clear_tab_to_focus();
Input *i = Location = new CustInput(0,0,0,0,0);
- i->tooltip("Location");
i->color(CuteColor);
i->when(WHEN_ENTER_KEY);
i->callback(location_cb, this);
@@ -440,14 +475,24 @@ PackedGroup *UI::make_location()
Search = b = new HighlightButton(0,0,16,22,0);
b->image(icons->ImgSearch);
- b->tooltip("Search the Web");
b->callback(search_cb, this);
b->clear_tab_to_focus();
+ Help = b = new HighlightButton(0,0,16,22,0);
+ b->image(icons->ImgHelp);
+ b->callback(help_cb, this);
+ b->clear_tab_to_focus();
+
pg->type(PackedGroup::ALL_CHILDREN_VERTICAL);
pg->resizable(i);
pg->end();
+ if (prefs.show_tooltip) {
+ Clear->tooltip("Clear the URL box.\nMiddle-click to paste a URL.");
+ Location->tooltip("Location");
+ Search->tooltip("Search the Web");
+ Help->tooltip("Help");
+ }
return pg;
}
@@ -494,7 +539,8 @@ Widget *UI::make_filemenu_button()
_MSG("UI::make_filemenu_button w=%d h=%d padding=%d\n", w, h, padding);
btn->box(PanelSize == P_large ? FLAT_BOX : THIN_UP_BOX);
btn->callback(filemenu_cb, this);
- btn->tooltip("File menu");
+ if (prefs.show_tooltip)
+ btn->tooltip("File menu");
btn->clear_tab_to_focus();
if (!prefs.show_filemenu && PanelSize != P_large)
btn->hide();
@@ -520,7 +566,7 @@ Group *UI::make_panel(int ww)
icons = &small_icons;
else
icons = &standard_icons;
-
+
if (PanelSize == P_tiny) {
if (Small_Icons)
xpos = 0, bw = 22, bh = 22, fh = 0, lh = 22, lbl = 0;
@@ -584,7 +630,7 @@ Group *UI::make_panel(int ww)
g2->resizable(pg);
g2->end();
-
+
// Toolbar
g3 = new Group(0,fh+lh,ww,bh);
g3->begin();
@@ -600,12 +646,12 @@ Group *UI::make_panel(int ww)
w = make_progress_bars(1,0);
}
pg->add(w);
-
+
g3->resizable(pg); // Better than 'w3' and it also works
pg->box(BORDER_FRAME);
//g3->box(EMBOSSED_BOX);
g3->end();
-
+
g1->resizable(g3);
g1->end();
}
@@ -614,16 +660,45 @@ Group *UI::make_panel(int ww)
}
/*
+ * Create the status panel
+ */
+Group *UI::make_status_panel(int ww)
+{
+ const int s_h = 20, bm_w = 16;
+ Group *g = new Group(0, 0, ww, s_h, 0);
+
+ // Status box
+ Status = new Output(0, 0, ww-bm_w, s_h, 0);
+ Status->value("");
+ Status->box(THIN_DOWN_BOX);
+ Status->clear_click_to_focus();
+ Status->clear_tab_to_focus();
+ Status->color(GRAY80);
+ g->add(Status);
+ //Status->throw_focus();
+
+ // Bug Meter
+ BugMeter = new HighlightButton(ww-bm_w,0,bm_w,s_h,0);
+ BugMeter->image(icons->ImgMeterOK);
+ BugMeter->box(THIN_DOWN_BOX);
+ BugMeter->align(ALIGN_INSIDE|ALIGN_CLIP|ALIGN_LEFT);
+ if (prefs.show_tooltip)
+ BugMeter->tooltip("Show HTML bugs\n(right-click for menu)");
+ BugMeter->callback(bugmeter_cb, this);
+ BugMeter->clear_tab_to_focus();
+ g->add(BugMeter);
+
+ g->resizable(Status);
+ return g;
+}
+
+/*
* User Interface constructor
- */
+ */
UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
Group(x, y, ww, wh, label)
{
- int s_h = 20;
-
- Font *f = font(prefs.vw_fontname, 0);
- if (f)
- this->labelfont(f);
+ PointerOnLink = FALSE;
Tabs = NULL;
TabTooltip = NULL;
@@ -631,12 +706,16 @@ UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
add(TopGroup);
resizable(TopGroup);
set_flag(RAW_LABEL);
-
+
if (cur_ui) {
PanelSize = cur_ui->PanelSize;
CuteColor = cur_ui->CuteColor;
Small_Icons = cur_ui->Small_Icons;
- Panelmode = cur_ui->Panelmode;
+ if (cur_ui->Panelmode == UI_HIDDEN ||
+ cur_ui->Panelmode == UI_TEMPORARILY_SHOW_PANELS)
+ Panelmode = UI_HIDDEN;
+ else
+ Panelmode = UI_NORMAL;
} else {
// Set some default values
//PanelSize = P_tiny, CuteColor = 26, Small_Icons = 0;
@@ -646,12 +725,10 @@ UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
Panelmode = (UIPanelmode) prefs.fullwindow_start;
}
-
// Control panel
Panel = make_panel(ww);
TopGroup->add(Panel);
-
// Render area
Main = new Widget(0,0,1,1,"Welcome...");
Main->box(FLAT_BOX);
@@ -666,47 +743,11 @@ UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
// Find text bar
findbar = new Findbar(ww, 28);
- TopGroup->add(findbar);
+ TopGroup->add(findbar);
// Status Panel
- StatusPanel = new Group(0, 0, ww, s_h, 0);
- // Status box
- int il_w = 16;
- int bm_w = 16;
- Status = new Output(0, 0, ww-bm_w-il_w, s_h, 0);
- Status->value("");
- Status->box(THIN_DOWN_BOX);
- Status->clear_click_to_focus();
- Status->clear_tab_to_focus();
- Status->color(GRAY80);
- StatusPanel->add(Status);
- //Status->throw_focus();
-
- // Image loading indicator
- ImageLoad = new HighlightButton(ww-il_w-bm_w,0,il_w,s_h,0);
- ImageLoad->type(Button::TOGGLE);
- ImageLoad->state(prefs.load_images);
- ImageLoad->image(icons->ImgImageLoadMulti);
-
- ImageLoad->box(THIN_DOWN_BOX);
- ImageLoad->align(ALIGN_INSIDE|ALIGN_CLIP|ALIGN_LEFT);
- ImageLoad->tooltip("Toggle image loading");
- ImageLoad->clear_tab_to_focus();
- StatusPanel->add(ImageLoad);
-
- // Bug Meter
- BugMeter = new HighlightButton(ww-bm_w,0,bm_w,s_h,0);
- BugMeter->image(icons->ImgMeterOK);
- BugMeter->box(THIN_DOWN_BOX);
- BugMeter->align(ALIGN_INSIDE|ALIGN_CLIP|ALIGN_LEFT);
- BugMeter->tooltip("Show HTML bugs\n(right-click for menu)");
- BugMeter->callback(bugmeter_cb, this);
- BugMeter->clear_tab_to_focus();
- StatusPanel->add(BugMeter);
-
- StatusPanel->resizable(Status);
-
- TopGroup->add(StatusPanel);
+ StatusPanel = make_status_panel(ww);
+ TopGroup->add(StatusPanel);
// Make the full screen button (to be attached to the viewport later)
// TODO: attach to the viewport
@@ -721,8 +762,6 @@ UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
Panel->hide();
StatusPanel->hide();
}
-
- //show();
}
/*
@@ -742,69 +781,102 @@ int UI::handle(int event)
_MSG("UI::handle event=%d (%d,%d)\n", event, event_x(), event_y());
_MSG("Panel->h()=%d Main->h()=%d\n", Panel->h() , Main->h());
- int ret = 0, k = event_key();
-
- // We're only interested in some flags
- unsigned modifier = event_state() & (SHIFT | CTRL | ALT);
+ int ret = 0;
if (event == KEY) {
return 0; // Receive as shortcut
-
} else if (event == SHORTCUT) {
- // Handle keyboard shortcuts here.
- if (modifier == CTRL) {
- if (k == 'b') {
- a_UIcmd_book(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 'f') {
- set_findbar_visibility(1);
- ret = 1;
- } else if (k == 'l') {
- focus_location();
- ret = 1;
- } else if (k == 'n') {
- a_UIcmd_browser_window_new(w(),h(),a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 'o') {
- a_UIcmd_open_file(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 'q') {
- a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 'r') {
- a_UIcmd_reload(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 's') {
- a_UIcmd_search_dialog(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 't') {
- a_UIcmd_open_url_nt(a_UIcmd_get_bw_by_widget(this), NULL, 1);
- ret = 1;
- } else if (k == ' ') {
- panelmode_cb_i();
- ret = 1;
- }
- } else if (modifier == ALT) {
- if (k == 'f') {
- a_UIcmd_file_popup(a_UIcmd_get_bw_by_widget(this), FileButton);
- } else if (k == 'q' && event_key_state(LeftAltKey)) {
- a_Timeout_add(0.0, a_UIcmd_close_all_bw, NULL);
+ KeysCommand_t cmd = Keys::getKeyCmd();
+ if (cmd == KEYS_NOP) {
+ // Do nothing
+ } else if (cmd == KEYS_SCREEN_UP || cmd == KEYS_SCREEN_DOWN ||
+ cmd == KEYS_LINE_UP || cmd == KEYS_LINE_DOWN ||
+ cmd == KEYS_LEFT || cmd == KEYS_RIGHT ||
+ cmd == KEYS_TOP || cmd == KEYS_BOTTOM) {
+ a_UIcmd_scroll(a_UIcmd_get_bw_by_widget(this), cmd);
+ ret = 1;
+ } else if (cmd == KEYS_BACK) {
+ a_UIcmd_back(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_FORWARD) {
+ a_UIcmd_forw(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_BOOKMARKS) {
+ a_UIcmd_book(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_FIND) {
+ set_findbar_visibility(1);
+ ret = 1;
+ } else if (cmd == KEYS_WEBSEARCH) {
+ a_UIcmd_search_dialog(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_GOTO) {
+ focus_location();
+ ret = 1;
+ } else if (cmd == KEYS_NEW_TAB) {
+ a_UIcmd_open_url_nt(a_UIcmd_get_bw_by_widget(this), NULL, 1);
+ ret = 1;
+ } else if (cmd == KEYS_CLOSE_TAB) {
+ a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_HIDE_PANELS &&
+ get_panelmode() == UI_TEMPORARILY_SHOW_PANELS) {
+ set_panelmode(UI_HIDDEN);
+ ret = 1;
+ } else if (cmd == KEYS_NEW_WINDOW) {
+ a_UIcmd_browser_window_new(w(),h(),0,a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_OPEN) {
+ a_UIcmd_open_file(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_HOME) {
+ a_UIcmd_home(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_RELOAD) {
+ a_UIcmd_reload(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_STOP) {
+ a_UIcmd_stop(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_SAVE) {
+ a_UIcmd_save(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_FULLSCREEN) {
+ panelmode_cb_i();
+ ret = 1;
+ } else if (cmd == KEYS_FILE_MENU) {
+ a_UIcmd_file_popup(a_UIcmd_get_bw_by_widget(this), FileButton);
+ ret = 1;
+ } else if (cmd == KEYS_CLOSE_ALL) {
+ a_Timeout_add(0.0, a_UIcmd_close_all_bw, NULL);
+ ret = 1;
+ }
+ } else if (event == PUSH) {
+ if (prefs.middle_click_drags_page == 0 &&
+ event_button() == MiddleButton &&
+ !a_UIcmd_pointer_on_link(a_UIcmd_get_bw_by_widget(this))) {
+ if (Main->Rectangle::contains (event_x (), event_y ())) {
+ /* Offer the event to Main's children (form widgets) */
+ int save_x = e_x, save_y = e_y;
+
+ e_x -= Main->x();
+ e_y -= Main->y();
+ ret = ((Group *)Main)->Group::handle(event);
+ e_x = save_x;
+ e_y = save_y;
}
- } else {
- // Back and Forward navigation shortcuts
- if (modifier == 0 && (k == BackSpaceKey || k == ',')) {
- a_UIcmd_back(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if ((modifier == 0 && k == '.') ||
- (modifier == SHIFT && k == BackSpaceKey)) {
- a_UIcmd_forw(a_UIcmd_get_bw_by_widget(this));
+ if (!ret) {
+ /* middle click was not on a link or a form widget */
+ paste_url();
ret = 1;
}
}
}
- if (!ret)
+ if (!ret) {
ret = Group::handle(event);
+ }
+
return ret;
}
@@ -898,7 +970,7 @@ void UI::set_img_prog(int n_img, int t_img, int cmd)
} else {
IProg->activate();
if (cmd == 1) {
- snprintf(str, 32, "%s%d of %d",
+ snprintf(str, 32, "%s%d of %d",
(PanelSize == 0) ? "" : "Images\n", n_img, t_img);
} else if (cmd == 2) {
str[0] = '\0';
@@ -926,9 +998,7 @@ void UI::set_bug_prog(int n_bug)
BugMeter->redraw_label();
new_w = strlen(str)*8 + 20;
}
- Status->resize(0,0,StatusPanel->w()-ImageLoad->w()-new_w,Status->h());
- ImageLoad->resize(StatusPanel->w()-ImageLoad->w()-new_w, 0, ImageLoad->w(),
- ImageLoad->h());
+ Status->resize(0,0,StatusPanel->w()-new_w,Status->h());
BugMeter->resize(StatusPanel->w()-new_w, 0, new_w, BugMeter->h());
StatusPanel->init_sizes();
}
@@ -954,12 +1024,16 @@ void UI::customize(int flags)
Stop->hide();
if ( !prefs.show_bookmarks )
Bookmarks->hide();
+ if ( !prefs.show_tools )
+ Tools->hide();
if ( !prefs.show_clear_url )
Clear->hide();
if ( !prefs.show_url )
Location->hide();
if ( !prefs.show_search )
Search->hide();
+ if ( !prefs.show_help )
+ Help->hide();
if ( !prefs.show_progress_box )
ProgBox->hide();
}
@@ -987,7 +1061,8 @@ void UI::panel_cb_i()
*/
void UI::color_change_cb_i()
{
- static int ncolor = 0, cols[] = {7,17,26,51,140,156,205,206,215,-1};
+ const int cols[] = {7,17,26,51,140,156,205,206,215,-1};
+ static int ncolor = 0;
ncolor = (cols[ncolor+1] < 0) ? 0 : ncolor + 1;
CuteColor = cols[ncolor];
@@ -1048,32 +1123,28 @@ void UI::set_render_layout(Widget &nw)
}
/*
- * Set the window title
+ * Set the tab title
*/
-void UI::set_page_title(const char *label)
+void UI::set_tab_title(const char *label)
{
char title[128];
dReturn_if_fail(label != NULL);
- snprintf(title, 128, "Dillo: %s", label);
- this->window()->copy_label(title);
- this->window()->redraw_label();
+ if (*label) {
+ // Make a label for this tab
+ size_t tab_chars = 18, label_len = strlen(label);
- if (tabs() && *label) {
- size_t tab_chars = 18;
+ if (label_len > tab_chars)
+ tab_chars = a_Utf8_end_of_char(label, tab_chars - 1) + 1;
snprintf(title, tab_chars + 1, "%s", label);
- if (strlen(label) > tab_chars) {
- while (label[tab_chars] & 0x80 && !(label[tab_chars] & 0x40) &&
- tab_chars < 23) {
- // In the middle of a multibyte UTF-8 character.
- title[tab_chars] = label[tab_chars];
- tab_chars++;
- }
+ if (label_len > tab_chars)
snprintf(title + tab_chars, 4, "...");
+ // Avoid unnecessary redraws
+ if (strcmp(this->label(), title)) {
+ this->copy_label(title);
+ this->redraw_label();
}
- this->copy_label(title);
- this->redraw_label();
// Disabled because of a bug in fltk::Tabgroup
//dFree(TabTooltip);
diff --git a/src/ui.hh b/src/ui.hh
index 7a302efd..9e1b6d4f 100644
--- a/src/ui.hh
+++ b/src/ui.hh
@@ -16,8 +16,6 @@
#include "findbar.hh"
-using namespace fltk;
-
typedef enum {
UI_BACK = 0,
UI_FORW,
@@ -26,6 +24,7 @@ typedef enum {
UI_SAVE,
UI_STOP,
UI_BOOK,
+ UI_TOOLS,
UI_CLEAR,
UI_SEARCH
} UIButton;
@@ -47,15 +46,15 @@ class UI : public fltk::Group {
CustTabGroup *Tabs;
char *TabTooltip;
- Group *TopGroup;
- Button *Back, *Forw, *Home, *Reload, *Save, *Stop, *Bookmarks,
- *Clear, *Search, *FullScreen, *ImageLoad, *BugMeter, *FileButton;
- Input *Location;
- PackedGroup *ProgBox;
+ fltk::Group *TopGroup;
+ fltk::Button *Back, *Forw, *Home, *Reload, *Save, *Stop, *Bookmarks, *Tools,
+ *Clear, *Search, *Help, *FullScreen, *BugMeter, *FileButton;
+ fltk::Input *Location;
+ fltk::PackedGroup *ProgBox;
CustProgressBox *PProg, *IProg;
- Group *Panel, *StatusPanel;
- Widget *Main;
- Output *Status;
+ fltk::Group *Panel, *StatusPanel;
+ fltk::Widget *Main;
+ fltk::Output *Status;
int MainIdx;
// Panel customization variables
@@ -64,13 +63,15 @@ class UI : public fltk::Group {
UIPanelmode Panelmode;
Findbar *findbar;
+ int PointerOnLink;
- PackedGroup *make_toolbar(int tw, int th);
- PackedGroup *make_location();
- PackedGroup *make_progress_bars(int wide, int thin_up);
+ fltk::PackedGroup *make_toolbar(int tw, int th);
+ fltk::PackedGroup *make_location();
+ fltk::PackedGroup *make_progress_bars(int wide, int thin_up);
void make_menubar(int x, int y, int w, int h);
- Widget *make_filemenu_button();
- Group *make_panel(int ww);
+ fltk::Widget *make_filemenu_button();
+ fltk::Group *make_panel(int ww);
+ fltk::Group *make_status_panel(int ww);
public:
@@ -89,20 +90,20 @@ public:
void set_img_prog(int n_img, int t_img, int cmd);
void set_bug_prog(int n_bug);
void set_render_layout(Widget &nw);
- void set_page_title(const char *label);
+ void set_tab_title(const char *label);
void customize(int flags);
void button_set_sens(UIButton btn, int sens);
void paste_url();
void set_panelmode(UIPanelmode mode);
UIPanelmode get_panelmode();
void set_findbar_visibility(bool visible);
- bool images_enabled() { return ImageLoad->state();}
- void images_enabled(int flag) { ImageLoad->state(flag);}
Widget *fullscreen_button() { return FullScreen; }
void fullscreen_toggle() { FullScreen->do_callback(); }
CustTabGroup *tabs() { return Tabs; }
void tabs(CustTabGroup *tabs) { Tabs = tabs; }
+ int pointerOnLink() { return PointerOnLink; }
+ void pointerOnLink(int flag) { PointerOnLink = flag; }
// Hooks to method callbacks
void panel_cb_i();
diff --git a/src/uicmd.cc b/src/uicmd.cc
index 564e9957..93836735 100644
--- a/src/uicmd.cc
+++ b/src/uicmd.cc
@@ -15,15 +15,22 @@
#include <stdio.h>
#include <stdarg.h>
#include <math.h> /* for rint */
+
+#include <fltk/draw.h>
+#include <fltk/damage.h>
#include <fltk/Widget.h>
#include <fltk/TabGroup.h>
+#include <fltk/Tooltip.h>
-#include "dir.h"
+#include "paths.hh"
+#include "keys.hh"
#include "ui.hh"
#include "uicmd.hh"
#include "timeout.hh"
+#include "utf8.hh"
#include "menu.hh"
#include "dialog.hh"
+#include "xembed.hh"
#include "bookmark.h"
#include "history.h"
#include "msg.h"
@@ -33,6 +40,8 @@
#include "nav.h"
+#define DEFAULT_TAB_LABEL "Dillo"
+
// Handy macro
#define BW2UI(bw) ((UI*)((bw)->ui))
@@ -49,20 +58,198 @@ static char *save_dir = NULL;
using namespace fltk;
-//
-// For custom handling of keyboard
-//
+
+//----------------------------------------------------------------------------
+#define BTN_W 25
+#define BTN_H 20
+
+static int btn_x;
+
+/*
+ * Adds a tab-close button at the rightmost part
+ */
+class CustShrinkTabPager : public TabGroupPager {
+ bool btn_hl;
+ TabGroup *tg;
+public:
+ int update_positions(
+ TabGroup *g, int numchildren, int &selected,
+ int &cumulated_width, int &available_width,
+ int *tab_pos, int *tab_width);
+ virtual int which(TabGroup* g, int m_x,int m_y);
+ virtual TabGroupPager* clone() const;
+ virtual const char * mode_name() const {return "Shrink";}
+ virtual int id() const {return PAGER_SHRINK;}
+ virtual int available_width(TabGroup *g) const;
+ virtual bool draw_tabs(TabGroup* g, int selected, int* tab_pos,
+ int* tab_width) {
+ if (!tg) tg = g;
+ if (g->children() > 1) {
+ fltk::Rectangle r(btn_x,0,BTN_W,BTN_H);
+ setcolor(btn_hl ? 206 : GRAY75);
+ fillrect(r);
+ if (btn_hl) {
+ setcolor(WHITE);
+ strokerect(r);
+ }
+ setcolor(GRAY10);
+ //fltk::setfont(fltk::getfont()->bold(), fltk::getsize());
+ r.h(r.h()-2);
+ drawtext("X", r, ALIGN_CENTER);
+ return false;
+ } else {
+ // WORKAROUND: for http://fltk.org/str.php?L2062
+ // By returning true we avoid a call to TabGroup::draw_tab()
+ // in TabGroup::draw() in case we don't show the tabs.
+ return true;
+ }
+ }
+
+ void btn_highlight(bool flag) {
+ if (btn_hl != flag) {
+ btn_hl = flag;
+ if (tg)
+ tg->redraw(DAMAGE_VALUE);
+ }
+ };
+ bool btn_highlight() { return btn_hl; };
+
+ CustShrinkTabPager() : TabGroupPager() {
+ noclip(true);
+ btn_hl = false;
+ tg = NULL;
+ }
+};
+
+int CustShrinkTabPager::available_width(TabGroup *g) const
+{
+ _MSG("CustShrinkTabPager::available_width\n");
+ int w = MAX (g->w() - this->slope()-1 - BTN_W, 0);
+ btn_x = w + 6;
+ return w;
+}
+
+int CustShrinkTabPager::which(TabGroup* g, int event_x,int event_y)
+{
+ int H = g->tab_height();
+ if (!H) return -1;
+ if (H < 0) {
+ if (event_y > g->h() || event_y < g->h()+H) return -1;
+ } else {
+ if (event_y > H || event_y < 0) return -1;
+ }
+ if (event_x < 0) return -1;
+ int p[128], w[128];
+ int selected = g->tab_positions(p, w);
+ int d = (event_y-(H>=0?0:g->h()))*slope()/H;
+ for (int i=0; i<g->children(); i++) {
+ if (event_x < p[i+1]+(i<selected ? slope() - d : d)) return i;
+ }
+ return -1;
+}
+
+/*
+ * Prevents tabs from going over the close-tab button.
+ * Modified from fltk-2.0.x-r6525.
+ */
+int CustShrinkTabPager::update_positions(
+ TabGroup *g, int numchildren, int &selected,
+ int &cumulated_width, int &available_width,
+ int *tab_pos, int *tab_width)
+{
+ available_width-=BTN_W;
+
+ // uh oh, they are too big, we must move them:
+ // special case when the selected tab itself is too big, make it fill
+ // cumulated_width:
+ int i;
+
+ if (tab_width[selected] >= available_width) {
+ tab_width[selected] = available_width;
+ for (i = 0; i <= selected; i++)
+ tab_pos[i] = 0;
+ for (i = selected + 1; i <= numchildren; i++)
+ tab_pos[i] = available_width;
+ return selected;
+ }
+
+ int w2[128];
+
+ for (i = 0; i < numchildren; i++)
+ w2[i] = tab_width[i];
+ i = numchildren - 1;
+ int j = 0;
+
+ int minsize = 5;
+
+ bool right = true;
+
+ while (cumulated_width > available_width) {
+ int n; // which one to shrink
+
+ if (j < selected && (!right || i <= selected)) { // shrink a left one
+ n = j++;
+ right = true;
+ } else if (i > selected) { // shrink a right one
+ n = i--;
+ right = false;
+ } else { // no more space, start making them zero
+ minsize = 0;
+ i = numchildren - 1;
+ j = 0;
+ right = true;
+ continue;
+ }
+ cumulated_width -= w2[n] - minsize;
+ w2[n] = minsize;
+ if (cumulated_width < available_width) {
+ w2[n] = available_width - cumulated_width + minsize;
+ cumulated_width = available_width;
+ break;
+ }
+ }
+ // re-sum the positions:
+ cumulated_width = 0;
+ for (i = 0; i < numchildren; i++) {
+ cumulated_width += w2[i];
+ tab_pos[i+1] = cumulated_width;
+ }
+ return selected;
+}
+
+TabGroupPager* CustShrinkTabPager::clone() const {
+ return new CustShrinkTabPager(*this);
+}
+
+//----------------------------------------------------------------------------
+
+/*
+ * For custom handling of keyboard
+ */
class CustTabGroup : public fltk::TabGroup {
+ Tooltip *toolTip;
+ bool tooltipEnabled;
+ bool buttonPushed;
public:
CustTabGroup (int x, int y, int ww, int wh, const char *lbl=0) :
- TabGroup(x,y,ww,wh,lbl) {};
+ TabGroup(x,y,ww,wh,lbl) {
+ // The parameter pager is cloned, so free it.
+ CustShrinkTabPager *cp = new CustShrinkTabPager();
+ this->pager(cp);
+ delete cp;
+ toolTip = new Tooltip;
+ tooltipEnabled = false;
+ buttonPushed = false;
+ };
+ ~CustTabGroup() { delete toolTip; }
int handle(int e) {
// Don't focus with arrow keys
_MSG("CustTabGroup::handle %d\n", e);
- int k = event_key();
- // We're only interested in some flags
- unsigned modifier = event_state() & (SHIFT | CTRL | ALT);
+ fltk::Rectangle r(btn_x,0,BTN_W,BTN_H);
if (e == KEY) {
+ int k = event_key();
+ // We're only interested in some flags
+ unsigned modifier = event_state() & (SHIFT | CTRL | ALT);
if (k == UpKey || k == DownKey || k == TabKey) {
return 0;
} else if (k == LeftKey || k == RightKey) {
@@ -74,20 +261,77 @@ public:
return 1;
}
// Avoid focus change.
- return 0;
+ return 0;
+ }
+ } else if (e == FOCUS_CHANGE) {
+ // Update the window title
+ BrowserWindow *bw = a_UIcmd_get_bw_by_widget(selected_child());
+ const char *title = a_History_get_title(NAV_TOP_UIDX(bw), 1);
+ a_UIcmd_set_page_title(bw, title ? title : "");
+ } else if (e == MOVE) {
+ CustShrinkTabPager *cstp = (CustShrinkTabPager *) pager();
+ if (event_inside(r) && children() > 1) {
+ /* We're inside the button area */
+ cstp->btn_highlight(true);
+ if (prefs.show_tooltip) {
+ /* Prepare the tooltip for pop-up */
+ tooltipEnabled = true;
+ /* We use parent() if available because we are returning 0.
+ * Returning without having TabGroup processing makes the
+ * popup event never reach 'this', but it reaches parent() */
+ toolTip->enter(parent() ?parent():this, r, "Close current Tab");
+ }
+ return 0; // Change focus
+ } else {
+ cstp->btn_highlight(false);
+
+ if (prefs.show_tooltip) {
+ /* Hide the tooltip or enable it again.*/
+ if (tooltipEnabled) {
+ tooltipEnabled = false;
+ toolTip->exit();
+ } else {
+ toolTip->enable();
+ }
+ }
+ }
+ } else if (e == PUSH && event_inside(r) &&
+ event_button() == 1 && children() > 1) {
+ buttonPushed = true;
+ return 1; /* non-zero */
+ } else if (e == RELEASE) {
+ if (event_inside(r) && event_button() == 1 &&
+ children() > 1 && buttonPushed) {
+ a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(selected_child()));
+ } else {
+ CustShrinkTabPager *cstp = (CustShrinkTabPager *) pager();
+ cstp->btn_highlight(false);
}
+ buttonPushed = false;
+ } else if (e == DRAG) {
+ /* Ignore this event */
+ return 1;
}
- return TabGroup::handle(e);
+ int ret = TabGroup::handle(e);
+
+ if (e == PUSH) {
+ /* WORKAROUND: FLTK raises the window on unhandled clicks,
+ * which we do not want.
+ */
+ ret = 1;
+ }
+ return ret;
}
void remove (Widget *w) {
TabGroup::remove (w);
/* fixup resizable in case we just removed it */
- if (resizable () == w)
+ if (resizable () == w) {
if (children () > 0)
resizable (child (children () - 1));
else
resizable (NULL);
+ }
if (children () < 2)
hideLabels ();
@@ -110,11 +354,12 @@ public:
}
};
+//----------------------------------------------------------------------------
+
static void win_cb (fltk::Widget *w, void *cb_data) {
- CustTabGroup *tabs;
int choice = 0;
+ CustTabGroup *tabs = (CustTabGroup*) cb_data;
- tabs = BW2UI((BrowserWindow*) cb_data)->tabs();
if (tabs->children () > 1)
choice = a_Dialog_choice3 ("Window contains more than one tab.",
"Close all tabs", "Cancel", NULL);
@@ -155,10 +400,12 @@ void a_UIcmd_send_event_to_tabs_by_wid(int e, void *v_wid)
* Create a new UI and its associated BrowserWindow data structure.
* Use style from v_ui. If non-NULL it must be of type UI*.
*/
-BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
+BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh,
+ uint32_t xid, const void *vbw)
{
BrowserWindow *old_bw = (BrowserWindow*)vbw;
BrowserWindow *new_bw = NULL;
+ Window *win;
if (ww <= 0 || wh <= 0) {
// Set default geometry from dillorc.
@@ -166,7 +413,11 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
wh = prefs.height;
}
- Window *win = new Window(ww, wh);
+ if (xid)
+ win = new Xembed(xid, ww, wh);
+ else
+ win = new Window(ww, wh);
+
win->shortcut(0); // Ignore Escape
if (prefs.buffered_drawing != 2)
win->clear_double_buffer();
@@ -177,7 +428,8 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
win->add(DilloTabs);
// Create and set the UI
- UI *new_ui = new UI(0, 0, ww, wh, "Label", old_bw ? BW2UI(old_bw) : NULL);
+ UI *new_ui = new UI(0, 0, ww, wh, DEFAULT_TAB_LABEL,
+ old_bw ? BW2UI(old_bw) : NULL);
new_ui->set_status("http://www.dillo.org/");
new_ui->tabs(DilloTabs);
@@ -203,7 +455,7 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
viewport->setBufferedDrawing (true);
else
viewport->setBufferedDrawing (false);
-
+
layout->attachView (viewport);
new_ui->set_render_layout(*viewport);
@@ -217,7 +469,9 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
// Copy the layout pointer into the bw data
new_bw->render_layout = (void*)layout;
- win->callback(win_cb, new_bw);
+ win->callback(win_cb, DilloTabs);
+
+ new_ui->focus_location();
return new_bw;
}
@@ -226,7 +480,7 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
* Create a new Tab.
* i.e the new UI and its associated BrowserWindow data structure.
*/
-BrowserWindow *UIcmd_tab_new(const void *vbw)
+static BrowserWindow *UIcmd_tab_new(const void *vbw)
{
_MSG(" UIcmd_tab_new vbw=%p\n", vbw);
@@ -239,10 +493,10 @@ BrowserWindow *UIcmd_tab_new(const void *vbw)
// WORKAROUND: limit the number of tabs because of a fltk bug
if (ui->tabs()->children() >= 127)
return a_UIcmd_browser_window_new(ui->window()->w(), ui->window()->h(),
- vbw);
+ 0, vbw);
// Create and set the UI
- UI *new_ui = new UI(0, 0, ui->w(), ui->h(), "Label", ui);
+ UI *new_ui = new UI(0, 0, ui->w(), ui->h(), DEFAULT_TAB_LABEL, ui);
new_ui->tabs(ui->tabs());
new_ui->tabs()->add(new_ui);
@@ -255,7 +509,7 @@ BrowserWindow *UIcmd_tab_new(const void *vbw)
Layout *layout = new Layout (platform);
FltkViewport *viewport = new FltkViewport (0, 0, 1, 1);
-
+
layout->attachView (viewport);
new_ui->set_render_layout(*viewport);
@@ -282,10 +536,11 @@ void a_UIcmd_close_bw(void *vbw)
Layout *layout = (Layout*)bw->render_layout;
MSG("a_UIcmd_close_bw\n");
- a_Bw_stop_clients(bw, BW_Root + BW_Img + Bw_Force);
+ a_Bw_stop_clients(bw, BW_Root + BW_Img + BW_Force);
delete(layout);
if (ui->tabs()) {
ui->tabs()->remove(ui);
+ ui->tabs()->value(ui->tabs()->children() - 1);
if (ui->tabs()->value() != -1)
ui->tabs()->selected_child()->take_focus();
else
@@ -332,7 +587,7 @@ void a_UIcmd_open_urlstr(void *vbw, const char *urlstr)
/* file URI */
ch = new_urlstr[5];
if (!ch || ch == '.') {
- url = a_Url_new(a_Dir_get_owd(), "file:");
+ url = a_Url_new(Paths::getOldWorkingDir(), "file:");
} else if (ch == '~') {
url = a_Url_new(dGethomedir(), "file:");
} else {
@@ -346,13 +601,10 @@ void a_UIcmd_open_urlstr(void *vbw, const char *urlstr)
dFree(new_urlstr);
if (url) {
- a_Nav_push(bw, url);
+ a_UIcmd_open_url(bw, url);
a_Url_free(url);
}
}
-
- /* let the rendered area have focus */
- //gtk_widget_grab_focus(GTK_BIN(bw->render_main_scroll)->child);
}
/*
@@ -360,15 +612,37 @@ void a_UIcmd_open_urlstr(void *vbw, const char *urlstr)
*/
void a_UIcmd_open_url(BrowserWindow *bw, const DilloUrl *url)
{
- a_Nav_push(bw, url);
+ a_Nav_push(bw, url, NULL);
+ if (BW2UI(bw)->get_panelmode() == UI_TEMPORARILY_SHOW_PANELS)
+ BW2UI(bw)->set_panelmode(UI_HIDDEN);
+ a_UIcmd_focus_main_area(bw);
+}
+
+static void UIcmd_open_url_nbw(BrowserWindow *new_bw, const DilloUrl *url)
+{
+ /* When opening a new BrowserWindow (tab or real window) we focus
+ * Location if we don't yet have an URL, main otherwise.
+ */
+ if (url) {
+ a_Nav_push(new_bw, url, NULL);
+ BW2UI(new_bw)->focus_main();
+ } else {
+ BW2UI(new_bw)->focus_location();
+ }
}
/*
- * Open a new URL in the given browser window
+ * Open a new URL in a new browser window
*/
void a_UIcmd_open_url_nw(BrowserWindow *bw, const DilloUrl *url)
{
- a_Nav_push_nw(bw, url);
+ int w, h;
+ BrowserWindow *new_bw;
+
+ a_UIcmd_get_wh(bw, &w, &h);
+ new_bw = a_UIcmd_browser_window_new(w, h, 0, bw);
+
+ UIcmd_open_url_nbw(new_bw, url);
}
/*
@@ -377,12 +651,11 @@ void a_UIcmd_open_url_nw(BrowserWindow *bw, const DilloUrl *url)
void a_UIcmd_open_url_nt(void *vbw, const DilloUrl *url, int focus)
{
BrowserWindow *new_bw = UIcmd_tab_new(vbw);
- if (url)
- a_Nav_push(new_bw, url);
- if (focus) {
+
+ if (focus)
BW2UI(new_bw)->tabs()->selected_child(BW2UI(new_bw));
- BW2UI(new_bw)->tabs()->selected_child()->take_focus();
- }
+
+ UIcmd_open_url_nbw(new_bw, url);
}
/*
@@ -422,7 +695,7 @@ void a_UIcmd_forw_popup(void *vbw)
*/
void a_UIcmd_home(void *vbw)
{
- a_Nav_home((BrowserWindow*)vbw);
+ a_UIcmd_open_url((BrowserWindow*)vbw, prefs.home);
}
/*
@@ -434,19 +707,44 @@ void a_UIcmd_reload(void *vbw)
}
/*
+ * Repush current URL
+ */
+void a_UIcmd_repush(void *vbw)
+{
+ a_Nav_repush((BrowserWindow*)vbw);
+}
+
+/*
+ * Zero-delay URL redirection.
+ */
+void a_UIcmd_redirection0(void *vbw, const DilloUrl *url)
+{
+ a_Nav_redirection0((BrowserWindow*)vbw, url);
+}
+
+/*
* Return a suitable filename for a given URL path.
*/
static char *UIcmd_make_save_filename(const char *pathstr)
{
size_t MaxLen = 64;
- char *FileName, *name;
- const char *dir = a_UIcmd_get_save_dir();
+ char *FileName, *newname, *o, *n;
+ const char *name, *dir = a_UIcmd_get_save_dir();
if ((name = strrchr(pathstr, '/'))) {
if (strlen(++name) > MaxLen) {
name = name + strlen(name) - MaxLen;
}
- FileName = dStrconcat(dir ? dir : "", name, NULL);
+ /* Replace %20 and ' ' with '_' in Filename */
+ o = n = newname = dStrdup(name);
+ for (int i = 0; o[i]; i++) {
+ *n++ = (o[i] == ' ') ? '_' :
+ (o[i] == '%' && o[i+1] == '2' && o[i+2] == '0') ?
+ i+=2, '_' : o[i];
+ }
+ *n = 0;
+ FileName = dStrconcat(dir ? dir : "", newname, NULL);
+ dFree(newname);
} else {
FileName = dStrconcat(dir ? dir : "", pathstr, NULL);
}
@@ -466,7 +764,7 @@ const char *a_UIcmd_get_save_dir()
*/
void a_UIcmd_set_save_dir(const char *dir)
{
- char *p;
+ const char *p;
if (dir && (p = strrchr(dir, '/'))) {
dFree(save_dir);
@@ -481,24 +779,21 @@ void a_UIcmd_set_save_dir(const char *dir)
void a_UIcmd_save(void *vbw)
{
const char *name;
- char *SuggestedName, *urlstr;
- DilloUrl *url;
+ char *SuggestedName;
+ BrowserWindow *bw = (BrowserWindow *)vbw;
+ const DilloUrl *url = a_History_get_url(NAV_TOP_UIDX(bw));
- a_UIcmd_set_save_dir(prefs.save_dir);
+ if (url) {
+ a_UIcmd_set_save_dir(prefs.save_dir);
+ SuggestedName = UIcmd_make_save_filename(URL_PATH(url));
+ name = a_Dialog_save_file("Save Page as File", NULL, SuggestedName);
+ MSG("a_UIcmd_save: %s\n", name);
+ dFree(SuggestedName);
- urlstr = a_UIcmd_get_location_text((BrowserWindow*)vbw);
- url = a_Url_new(urlstr, NULL);
- SuggestedName = UIcmd_make_save_filename(URL_PATH(url));
- name = a_Dialog_save_file("Save Page as File", NULL, SuggestedName);
- MSG("a_UIcmd_save: %s\n", name);
- dFree(SuggestedName);
- dFree(urlstr);
-
- if (name) {
- a_Nav_save_url((BrowserWindow*)vbw, url, name);
+ if (name) {
+ a_Nav_save_url(bw, url, name);
+ }
}
-
- a_Url_free(url);
}
/*
@@ -519,11 +814,19 @@ void a_UIcmd_stop(void *vbw)
MSG("a_UIcmd_stop()\n");
a_Nav_cancel_expect(bw);
- a_Bw_stop_clients(bw, BW_Root + BW_Img + Bw_Force);
+ a_Bw_stop_clients(bw, BW_Root + BW_Img + BW_Force);
a_UIcmd_set_buttons_sens(bw);
}
/*
+ * Popup the tools menu
+ */
+void a_UIcmd_tools(void *vbw, void *v_wid)
+{
+ a_Menu_tools_popup((BrowserWindow*)vbw, v_wid);
+}
+
+/*
* Open URL with dialog chooser
*/
void a_UIcmd_open_file(void *vbw)
@@ -535,7 +838,7 @@ void a_UIcmd_open_file(void *vbw)
if (name) {
url = a_Url_new(name, "file:");
- a_Nav_push((BrowserWindow*)vbw, url);
+ a_UIcmd_open_url((BrowserWindow*)vbw, url);
a_Url_free(url);
dFree(name);
}
@@ -579,11 +882,12 @@ static char *UIcmd_make_search_str(const char *str)
*/
void a_UIcmd_search_dialog(void *vbw)
{
- const char *query, *url_str;
+ const char *query;
if ((query = a_Dialog_input("Search the Web:"))) {
- url_str = UIcmd_make_search_str(query);
+ char *url_str = UIcmd_make_search_str(query);
a_UIcmd_open_urlstr(vbw, url_str);
+ dFree(url_str);
}
}
@@ -625,7 +929,7 @@ void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url)
void a_UIcmd_book(void *vbw)
{
DilloUrl *url = a_Url_new("dpi:/bm/", NULL);
- a_Nav_push((BrowserWindow*)vbw, url);
+ a_UIcmd_open_url((BrowserWindow*)vbw, url);
a_Url_free(url);
}
@@ -641,10 +945,11 @@ void a_UIcmd_add_bookmark(BrowserWindow *bw, const DilloUrl *url)
/*
* Popup the page menu
*/
-void a_UIcmd_page_popup(void *vbw, const DilloUrl *url,
- bool_t has_bugs, bool_t unloaded_imgs)
+void a_UIcmd_page_popup(void *vbw, bool_t has_bugs, void *v_cssUrls)
{
- a_Menu_page_popup((BrowserWindow*)vbw, url, has_bugs, unloaded_imgs);
+ BrowserWindow *bw = (BrowserWindow*)vbw;
+ const DilloUrl *url = a_History_get_url(NAV_TOP_UIDX(bw));
+ a_Menu_page_popup(bw, url, has_bugs, v_cssUrls);
}
/*
@@ -659,9 +964,18 @@ void a_UIcmd_link_popup(void *vbw, const DilloUrl *url)
* Pop up the image menu
*/
void a_UIcmd_image_popup(void *vbw, const DilloUrl *url, bool_t loaded_img,
- DilloUrl *link_url)
+ DilloUrl *page_url, DilloUrl *link_url)
{
- a_Menu_image_popup((BrowserWindow*)vbw, url, loaded_img, link_url);
+ a_Menu_image_popup((BrowserWindow*)vbw, url, loaded_img, page_url,link_url);
+}
+
+/*
+ * Pop up the form menu
+ */
+void a_UIcmd_form_popup(void *vbw, const DilloUrl *url, void *vform,
+ bool_t showing_hiddens)
+{
+ a_Menu_form_popup((BrowserWindow*)vbw, url, vform, showing_hiddens);
}
/*
@@ -682,17 +996,32 @@ void a_UIcmd_copy_urlstr(BrowserWindow *bw, const char *urlstr)
}
/*
- * Show a text window with the URL's source
+ * Ask the vsource dpi to show this URL's source
*/
-void a_UIcmd_view_page_source(const DilloUrl *url)
+void a_UIcmd_view_page_source(BrowserWindow *bw, const DilloUrl *url)
{
char *buf;
int buf_size;
+ Dstr *dstr_url;
+ DilloUrl *vs_url;
+ static int post_id = 0;
+ char tag[8];
if (a_Nav_get_buf(url, &buf, &buf_size)) {
- void *vWindow = a_Dialog_make_text_window(buf, "View Page source");
+ a_Nav_set_vsource_url(url);
+ dstr_url = dStr_new("dpi:/vsource/:");
+ dStr_append(dstr_url, URL_STR(url));
+ if (URL_FLAGS(url) & URL_Post) {
+ /* append a custom string to differentiate POST URLs */
+ post_id = (post_id < 9999) ? post_id + 1 : 0;
+ snprintf(tag, 8, "_%.4d", post_id);
+ dStr_append(dstr_url, tag);
+ }
+ vs_url = a_Url_new(dstr_url->str, NULL);
+ a_UIcmd_open_url_nt(bw, vs_url, 1);
+ a_Url_free(vs_url);
+ dStr_free(dstr_url, 1);
a_Nav_unref_buf(url);
- a_Dialog_show_text_window(vWindow);
}
}
@@ -704,9 +1033,7 @@ void a_UIcmd_view_page_bugs(void *vbw)
BrowserWindow *bw = (BrowserWindow*)vbw;
if (bw->num_page_bugs > 0) {
- void *vWindow = a_Dialog_make_text_window(bw->page_bugs->str,
- "Detected HTML errors");
- a_Dialog_show_text_window(vWindow);
+ a_Dialog_text_window(bw->page_bugs->str, "Detected HTML errors");
} else {
a_Dialog_msg("Zero detected HTML errors!");
}
@@ -805,6 +1132,40 @@ void a_UIcmd_set_scroll_by_fragment(BrowserWindow *bw, const char *f)
}
/*
+ * Pass scrolling command to dw.
+ */
+void a_UIcmd_scroll(BrowserWindow *bw, int icmd)
+{
+ Layout *layout = (Layout*)bw->render_layout;
+
+ if (layout) {
+ typedef struct {
+ KeysCommand_t keys_cmd;
+ ScrollCommand dw_cmd;
+ } mapping_t;
+
+ const mapping_t map[] = {
+ {KEYS_SCREEN_UP, SCREEN_UP_CMD},
+ {KEYS_SCREEN_DOWN, SCREEN_DOWN_CMD},
+ {KEYS_LINE_UP, LINE_UP_CMD},
+ {KEYS_LINE_DOWN, LINE_DOWN_CMD},
+ {KEYS_LEFT, LEFT_CMD},
+ {KEYS_RIGHT, RIGHT_CMD},
+ {KEYS_TOP, TOP_CMD},
+ {KEYS_BOTTOM, BOTTOM_CMD},
+ };
+ KeysCommand_t keycmd = (KeysCommand_t)icmd;
+
+ for (uint_t i = 0; i < (sizeof(map)/sizeof(mapping_t)); i++) {
+ if (keycmd == map[i].keys_cmd) {
+ layout->scroll(map[i].dw_cmd);
+ break;
+ }
+ }
+ }
+}
+
+/*
* Get location's text
*/
char *a_UIcmd_get_location_text(BrowserWindow *bw)
@@ -852,12 +1213,24 @@ void a_UIcmd_set_bug_prog(BrowserWindow *bw, int n_bug)
}
/*
- * Set the page title.
- * now it goes to the window titlebar (maybe to TAB label in the future).
+ * Set the page title in the window titlebar and tab label.
+ * (Update window titlebar for the current tab only)
*/
void a_UIcmd_set_page_title(BrowserWindow *bw, const char *label)
{
- BW2UI(bw)->set_page_title(label);
+ const int size = 128;
+ char title[size];
+
+ if (a_UIcmd_get_bw_by_widget(BW2UI(bw)->tabs()->selected_child()) == bw) {
+ // This is the focused bw, set window title
+ if (snprintf(title, size, "Dillo: %s", label) >= size) {
+ uint_t i = MIN(size - 4, 1 + a_Utf8_end_of_char(title, size - 8));
+ snprintf(title + i, 4, "...");
+ }
+ BW2UI(bw)->window()->copy_label(title);
+ BW2UI(bw)->window()->redraw_label();
+ }
+ BW2UI(bw)->set_tab_title(label);
}
/*
@@ -877,22 +1250,6 @@ void a_UIcmd_set_msg(BrowserWindow *bw, const char *format, ...)
}
/*
- * Check whether the UI has automatic image loading enabled.
- */
-bool_t a_UIcmd_get_images_enabled(BrowserWindow *bw)
-{
- return BW2UI(bw)->images_enabled();
-}
-
-/*
- * Enable/Disable automatic image loading.
- */
-void a_UIcmd_set_images_enabled(BrowserWindow *bw, int flag)
-{
- BW2UI(bw)->images_enabled(flag);
-}
-
-/*
* Set the sensitivity of back/forw/stop buttons.
*/
void a_UIcmd_set_buttons_sens(BrowserWindow *bw)
@@ -912,6 +1269,22 @@ void a_UIcmd_set_buttons_sens(BrowserWindow *bw)
}
/*
+ * Keep track of mouse pointer over a link.
+ */
+void a_UIcmd_set_pointer_on_link(BrowserWindow *bw, int flag)
+{
+ BW2UI(bw)->pointerOnLink(flag);
+}
+
+/*
+ * Is the mouse pointer over a link?
+ */
+int a_UIcmd_pointer_on_link(BrowserWindow *bw)
+{
+ return BW2UI(bw)->pointerOnLink();
+}
+
+/*
* Toggle control panel (aka. fullscreen)
*/
void a_UIcmd_fullscreen_toggle(BrowserWindow *bw)
@@ -920,23 +1293,24 @@ void a_UIcmd_fullscreen_toggle(BrowserWindow *bw)
}
/*
- * Search for next occurrence of key.
+ * Search for next/previous occurrence of key.
*/
-void a_UIcmd_findtext_search(BrowserWindow *bw, const char *key, int case_sens)
+void a_UIcmd_findtext_search(BrowserWindow *bw, const char *key,
+ int case_sens, int backwards)
{
Layout *l = (Layout *)bw->render_layout;
-
- switch (l->search(key, case_sens)) {
- case FindtextState::RESTART:
- a_UIcmd_set_msg(bw, "No further occurrences of \"%s\". "
- "Restarting from the top.", key);
- break;
- case FindtextState::NOT_FOUND:
- a_UIcmd_set_msg(bw, "\"%s\" not found.", key);
- break;
- case FindtextState::SUCCESS:
- default:
- a_UIcmd_set_msg(bw, "");
+
+ switch (l->search(key, case_sens, backwards)) {
+ case FindtextState::RESTART:
+ a_UIcmd_set_msg(bw, "No further occurrences of \"%s\". "
+ "Restarting from the top.", key);
+ break;
+ case FindtextState::NOT_FOUND:
+ a_UIcmd_set_msg(bw, "\"%s\" not found.", key);
+ break;
+ case FindtextState::SUCCESS:
+ default:
+ a_UIcmd_set_msg(bw, "");
}
}
diff --git a/src/uicmd.hh b/src/uicmd.hh
index 02dbae8a..c8fea9e7 100644
--- a/src/uicmd.hh
+++ b/src/uicmd.hh
@@ -8,7 +8,8 @@ extern "C" {
#endif /* __cplusplus */
-BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *v_bw);
+BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh,
+ uint32_t xid, const void *v_bw);
BrowserWindow *a_UIcmd_get_bw_by_widget(void *v_wid);
void a_UIcmd_send_event_to_tabs_by_wid(int e, void *v_wid);
void a_UIcmd_open_urlstr(void *vbw, const char *urlstr);
@@ -21,8 +22,11 @@ void a_UIcmd_forw(void *vbw);
void a_UIcmd_forw_popup(void *vbw);
void a_UIcmd_home(void *vbw);
void a_UIcmd_reload(void *vbw);
+void a_UIcmd_repush(void *vbw);
+void a_UIcmd_redirection0(void *vbw, const DilloUrl *url);
void a_UIcmd_save(void *vbw);
void a_UIcmd_stop(void *vbw);
+void a_UIcmd_tools(void *vbw, void *v_wid);
void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url);
void a_UIcmd_open_file(void *vbw);
const char *a_UIcmd_select_file();
@@ -32,18 +36,20 @@ void a_UIcmd_book(void *vbw);
void a_UIcmd_add_bookmark(BrowserWindow *bw, const DilloUrl *url);
void a_UIcmd_fullscreen_toggle(BrowserWindow *bw);
void a_UIcmd_findtext_dialog(BrowserWindow *bw);
-void a_UIcmd_findtext_search(BrowserWindow *bw,const char *key,int case_sens);
+void a_UIcmd_findtext_search(BrowserWindow *bw,const char *key,int case_sens,
+ int backwards);
void a_UIcmd_findtext_reset(BrowserWindow *bw);
void a_UIcmd_focus_main_area(BrowserWindow *bw);
void a_UIcmd_focus_location(void *vbw);
-void a_UIcmd_page_popup(void *vbw, const DilloUrl *url,
- bool_t has_bugs, bool_t unloaded_imgs);
+void a_UIcmd_page_popup(void *vbw, bool_t has_bugs, void *v_cssUrls);
void a_UIcmd_link_popup(void *vbw, const DilloUrl *url);
void a_UIcmd_image_popup(void *vbw, const DilloUrl *url, bool_t loaded_img,
- DilloUrl *link_url);
+ DilloUrl *page_url, DilloUrl *link_url);
+void a_UIcmd_form_popup(void *vbw, const DilloUrl *url, void *vform,
+ bool_t showing_hiddens);
void a_UIcmd_file_popup(void *vbw, void *v_wid);
void a_UIcmd_copy_urlstr(BrowserWindow *bw, const char *urlstr);
-void a_UIcmd_view_page_source(const DilloUrl *url);
+void a_UIcmd_view_page_source(BrowserWindow *bw, const DilloUrl *url);
void a_UIcmd_view_page_bugs(void *vbw);
void a_UIcmd_bugmeter_popup(void *vbw);
int *a_UIcmd_get_history(BrowserWindow *bw, int direction);
@@ -62,6 +68,7 @@ void a_UIcmd_get_wh(BrowserWindow *bw, int *w, int *h);
void a_UIcmd_get_scroll_xy(BrowserWindow *bw, int *x, int *y);
void a_UIcmd_set_scroll_xy(BrowserWindow *bw, int x, int y);
void a_UIcmd_set_scroll_by_fragment(BrowserWindow *bw, const char *f);
+void a_UIcmd_scroll(BrowserWindow *bw, int icmd);
char *a_UIcmd_get_location_text(BrowserWindow *bw);
void a_UIcmd_set_location_text(void *vbw, const char *text);
void a_UIcmd_set_page_prog(BrowserWindow *bw, size_t nbytes, int cmd);
@@ -69,11 +76,10 @@ void a_UIcmd_set_img_prog(BrowserWindow *bw, int n_img, int t_img, int cmd);
void a_UIcmd_set_bug_prog(BrowserWindow *bw, int n_bug);
void a_UIcmd_set_page_title(BrowserWindow *bw, const char *label);
void a_UIcmd_set_msg(BrowserWindow *bw, const char *format, ...);
-bool_t a_UIcmd_get_images_enabled(BrowserWindow *bw);
-void a_UIcmd_set_images_enabled(BrowserWindow *bw, int flag);
void a_UIcmd_set_buttons_sens(BrowserWindow *bw);
void a_UIcmd_fullscreen_toggle(BrowserWindow *bw);
-
+void a_UIcmd_set_pointer_on_link(BrowserWindow *bw, int flag);
+int a_UIcmd_pointer_on_link(BrowserWindow *bw);
#ifdef __cplusplus
}
diff --git a/src/url.c b/src/url.c
index 3fb9122a..acc8aa72 100644
--- a/src/url.c
+++ b/src/url.c
@@ -2,7 +2,7 @@
* File: url.c
*
* Copyright (C) 2001 Livio Baldini Soares <livio@linux.ime.usp.br>
- * Copyright (C) 2001-2007 Jorge Arellano Cid <jcid@dillo.org>
+ * Copyright (C) 2001-2009 Jorge Arellano Cid <jcid@dillo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
*/
/*
- * Regular Expression as given in RFC2396 for URL parsing.
+ * Regular Expression as given in RFC3986 for URL parsing.
*
* ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
* 12 3 4 5 6 7 8 9
@@ -42,8 +42,6 @@
* - path is never "undefined" though it may be "empty".
*/
-
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
@@ -114,7 +112,7 @@ const char *a_Url_hostname(const DilloUrl *u)
if ((p = strchr(url->authority, ':'))) {
url->port = strtol(p + 1, NULL, 10);
url->hostname = dStrndup(url->authority,
- (uint_t)(p - url->authority));
+ (uint_t)(p - url->authority));
} else {
url->hostname = url->authority;
}
@@ -190,6 +188,7 @@ static DilloUrl *Url_object_new(const char *uri_str)
/*
* Free a DilloUrl
+ * Do nothing if the argument is NULL
*/
void a_Url_free(DilloUrl *url)
{
@@ -206,7 +205,7 @@ void a_Url_free(DilloUrl *url)
}
/*
- * Resolve the URL as RFC2396 suggests.
+ * Resolve the URL as RFC3986 suggests.
*/
static Dstr *Url_resolve_relative(const char *RelStr,
DilloUrl *BaseUrlPar,
@@ -230,14 +229,19 @@ static Dstr *Url_resolve_relative(const char *RelStr,
SolvedUrl = dStr_sized_new(64);
Path = dStr_sized_new(64);
- /* path empty && scheme, authority and query undefined */
- if (!RelUrl->path && !RelUrl->scheme &&
- !RelUrl->authority && !RelUrl->query) {
+ /* path empty && scheme and authority undefined */
+ if (!RelUrl->path && !RelUrl->scheme && !RelUrl->authority) {
dStr_append(SolvedUrl, BaseStr);
-
+ if ((p = strchr(SolvedUrl->str, '#')))
+ dStr_truncate(SolvedUrl, p - SolvedUrl->str);
+
+ if (RelUrl->query) { /* query */
+ if (BaseUrl->query)
+ dStr_truncate(SolvedUrl, BaseUrl->query - BaseUrl->buffer - 1);
+ dStr_append_c(SolvedUrl, '?');
+ dStr_append(SolvedUrl, RelUrl->query);
+ }
if (RelUrl->fragment) { /* fragment */
- if (BaseUrl->fragment)
- dStr_truncate(SolvedUrl, BaseUrl->fragment-BaseUrl->buffer-1);
dStr_append_c(SolvedUrl, '#');
dStr_append(SolvedUrl, RelUrl->fragment);
}
@@ -252,14 +256,12 @@ static Dstr *Url_resolve_relative(const char *RelStr,
if (RelUrl->path)
dStr_append(Path, RelUrl->path);
- } else if (RelUrl->path && RelUrl->path[0] == '/') { /* path */
- dStr_append(Path, RelUrl->path);
-
} else {
- // solve relative path
- if (BaseUrl->path) {
+ if (RelUrl->path && RelUrl->path[0] == '/') { /* absolute path */
+ ; /* Ignore BaseUrl path */
+ } else if (BaseUrl->path) { /* relative path */
dStr_append(Path, BaseUrl->path);
- for (i = Path->len; --i >= 0 && Path->str[i] != '/'; );
+ for (i = Path->len; --i >= 0 && Path->str[i] != '/'; ) ;
if (Path->str[i] == '/')
dStr_truncate(Path, ++i);
}
@@ -278,14 +280,10 @@ static Dstr *Url_resolve_relative(const char *RelStr,
// erase "<segment>/../" and "<segment>/.."
s = p = Path->str;
while ( (p = strstr(p, "/..")) != NULL ) {
- if ((p[3] == '/' || !p[3]) && (p - s)) { // "/../" | "/.."
-
- for (e = p + 3 ; p[-1] != '/' && p > s; --p);
- if (p[0] != '.' || p[1] != '.' || p[2] != '/') {
- dStr_erase(Path, p - Path->str, e - p + (*e != 0));
- p -= (p > Path->str);
- } else
- p = e;
+ if (p[3] == '/' || !p[3]) { // "/../" | "/.."
+ for (e = p + 3 ; p > s && p[-1] != '/'; --p) ;
+ dStr_erase(Path, p - Path->str, e - p + (p > s && *e != 0));
+ p -= (p > Path->str);
} else
p += 3;
}
@@ -578,7 +576,7 @@ char *a_Url_decode_hex_str(const char *str)
*/
char *a_Url_encode_hex_str(const char *str)
{
- static const char *verbatim = "-_.*";
+ static const char *const verbatim = "-_.*";
char *newstr, *c;
if (!str)
@@ -587,7 +585,7 @@ char *a_Url_encode_hex_str(const char *str)
newstr = dNew(char, 6*strlen(str)+1);
for (c = newstr; *str; str++)
- if ((isalnum(*str) && !(*str & 0x80)) || strchr(verbatim, *str))
+ if ((dIsalnum(*str) && !(*str & 0x80)) || strchr(verbatim, *str))
/* we really need isalnum for the "C" locale */
*c++ = *str;
else if (*str == ' ')
@@ -611,7 +609,7 @@ char *a_Url_encode_hex_str(const char *str)
/*
- * RFC-2396 suggests this stripping when "importing" URLs from other media.
+ * RFC-3986 suggests this stripping when "importing" URLs from other media.
* Strip: "URL:", enclosing < >, and embedded whitespace.
* (We also strip illegal chars: 00-1F and 7F)
*/
@@ -636,3 +634,121 @@ char *a_Url_string_strip_delimiters(const char *str)
}
return new_str;
}
+
+/*
+ * Is the provided hostname an IP address?
+ */
+static bool_t Url_host_is_ip(const char *host)
+{
+ uint_t len;
+
+ if (!host || !*host)
+ return FALSE;
+
+ len = strlen(host);
+
+ if (len == strspn(host, "0123456789.")) {
+ _MSG("an IPv4 address\n");
+ return TRUE;
+ }
+ if (*host == '[' &&
+ (len == strspn(host, "0123456789abcdefABCDEF:.[]"))) {
+ /* The precise format is shown in section 3.2.2 of rfc 3986 */
+ _MSG("an IPv6 address\n");
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * How many internal dots are in the public portion of this hostname?
+ * e.g., for "www.dillo.org", it is one because everything under "dillo.org",
+ * as a .org domain, is part of one organization.
+ *
+ * Of course this is only a simple and imperfect approximation of
+ * organizational boundaries.
+ */
+static uint_t Url_host_public_internal_dots(const char *host)
+{
+ uint_t ret = 1;
+
+ if (host) {
+ int start, after, tld_len;
+
+ /* We may be able to trust the format of the host string more than
+ * I am here. Trailing dots and no dots are real possibilities, though.
+ */
+ after = strlen(host);
+ if (after > 0 && host[after - 1] == '.')
+ after--;
+ start = after;
+ while (start > 0 && host[start - 1] != '.')
+ start--;
+ tld_len = after - start;
+
+ if (tld_len > 0) {
+ /* These TLDs were chosen by examining the current publicsuffix list
+ * in January 2010 and picking out those where it was simplest for
+ * them to describe the situation by beginning with a "*.[tld]" rule.
+ */
+ const char *const tlds[] = {"ar","au","bd","bn","bt","ck","cy","do",
+ "eg","er","et","fj","fk","gt","gu","id",
+ "il","jm","ke","kh","kw","ml","mm","mt",
+ "mz","ni","np","nz","om","pg","py","qa",
+ "sv","tr","uk","uy","ve","ye","yu","za",
+ "zm","zw"};
+ uint_t i, tld_num = sizeof(tlds) / sizeof(tlds[0]);
+
+ for (i = 0; i < tld_num; i++) {
+ if (strlen(tlds[i]) == (uint_t) tld_len &&
+ !dStrncasecmp(tlds[i], host + start, tld_len)) {
+ _MSG("TLD code matched %s\n", tlds[i]);
+ ret++;
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * Given a URL host string, return the portion that is public, i.e., the
+ * domain that is in a registry outside the organization.
+ * For 'www.dillo.org', that would be 'dillo.org'.
+ */
+const char *a_Url_host_find_public_suffix(const char *host)
+{
+ const char *s;
+ uint_t dots;
+
+ if (!host || !*host || Url_host_is_ip(host))
+ return host;
+
+ s = host;
+
+ while (s[1])
+ s++;
+
+ if (s > host && *s == '.') {
+ /* don't want to deal with trailing dot */
+ s--;
+ }
+
+ dots = Url_host_public_internal_dots(host);
+
+ /* With a proper host string, we should not be pointing to a dot now. */
+
+ while (s > host) {
+ if (s[-1] == '.') {
+ if (dots == 0)
+ break;
+ else
+ dots--;
+ }
+ s--;
+ }
+
+ _MSG("public suffix of %s is %s\n", host, s);
+ return s;
+}
diff --git a/src/url.h b/src/url.h
index 32b173e6..503eec60 100644
--- a/src/url.h
+++ b/src/url.h
@@ -10,7 +10,6 @@
#ifndef __URL_H__
#define __URL_H__
-#include <string.h> /* for strcmp */
#include "d_size.h"
#include "../dlib/dlib.h"
@@ -39,7 +38,7 @@
#define URL_ReloadPage (1 << 7)
#define URL_ReloadFromCache (1 << 8)
-#define URL_ReloadIncomplete (1 << 9)
+#define URL_IgnoreScroll (1 << 9)
#define URL_SpamSafe (1 << 10)
#define URL_MultipartEnc (1 << 11)
@@ -95,12 +94,12 @@ extern "C" {
struct _DilloUrl {
Dstr *url_string;
const char *buffer;
- const char *scheme; //
- const char *authority; //
- const char *path; // These are references only
- const char *query; // (no need to free them)
- const char *fragment; //
- const char *hostname; //
+ const char *scheme; /**/
+ const char *authority; /**/
+ const char *path; /* These are references only */
+ const char *query; /* (no need to free them) */
+ const char *fragment; /**/
+ const char *hostname; /**/
int port;
int flags;
Dstr *data; /* POST */
@@ -124,7 +123,7 @@ void a_Url_set_ismap_coords(DilloUrl *u, char *coord_str);
char *a_Url_decode_hex_str(const char *str);
char *a_Url_encode_hex_str(const char *str);
char *a_Url_string_strip_delimiters(const char *str);
-
+const char *a_Url_host_find_public_suffix(const char *host);
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/src/utf8.cc b/src/utf8.cc
new file mode 100644
index 00000000..0138c616
--- /dev/null
+++ b/src/utf8.cc
@@ -0,0 +1,102 @@
+/*
+ * File: utf8.c
+ *
+ * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <fltk/utf.h>
+
+#include "../dlib/dlib.h" /* TRUE/FALSE */
+#include "utf8.hh"
+
+// C++ functions with C linkage ----------------------------------------------
+
+/*
+ * Return index of the last byte of the UTF-8-encoded character that str + i
+ * points to or into.
+ */
+uint_t a_Utf8_end_of_char(const char *str, uint_t i)
+{
+ /* We can almost get what we want from utf8fwd(p+1,...)-1, but that
+ * does not work for the last character in a string, and the fn makes some
+ * assumptions that do not suit us.
+ * Here's something very simpleminded instead:
+ */
+ if (str && *str && (str[i] & 0x80)) {
+ int internal_bytes = (str[i] & 0x40) ? 0 : 1;
+
+ while (((str[i + 1] & 0xc0) == 0x80) && (++internal_bytes < 4))
+ i++;
+ }
+ return i;
+}
+
+/*
+ * Decode a single UTF-8-encoded character starting at p.
+ * The resulting Unicode value (in the range 0-0x10ffff) is returned,
+ * and len is set to the number of bytes in the UTF-8 encoding.
+ * Note that utf8decode(), if given non-UTF-8 data, will interpret
+ * it as ISO-8859-1 or CP1252 if possible.
+ */
+uint_t a_Utf8_decode(const char* str, const char* end, int* len)
+{
+ return utf8decode(str, end, len);
+}
+
+/*
+ * Write UTF-8 encoding of ucs into buf and return number of bytes written.
+ */
+int a_Utf8_encode(unsigned int ucs, char *buf)
+{
+ return utf8encode(ucs, buf);
+}
+
+/*
+ * Examine first srclen bytes of src.
+ * Return 0 if not legal UTF-8, 1 if all ASCII, 2 if all below 0x800,
+ * 3 if all below 0x10000, and 4 otherwise.
+ */
+int a_Utf8_test(const char* src, unsigned int srclen)
+{
+ return utf8test(src, srclen);
+}
+
+/*
+ * Does s point to a UTF-8-encoded ideographic character?
+ *
+ * This is based on http://unicode.org/reports/tr14/#ID plus some guesses
+ * for what might make the most sense for Dillo. Surprisingly, they include
+ * Hangul Compatibility Jamo, but they're the experts, so I'll follow along.
+ */
+bool_t a_Utf8_ideographic(const char *s, const char *end, int *len)
+{
+ bool_t ret = FALSE;
+
+ if ((uchar_t)*s >= 0xe2) {
+ /* Unicode char >= U+2000. */
+ unsigned unicode = a_Utf8_decode(s, end, len);
+
+ if (unicode >= 0x2e80 &&
+ ((unicode <= 0xa4cf) ||
+ (unicode >= 0xf900 && unicode <= 0xfaff) ||
+ (unicode >= 0xff00 && unicode <= 0xff9f))) {
+ ret = TRUE;
+ }
+ } else {
+ *len = 1 + (int)a_Utf8_end_of_char(s, 0);
+ }
+ return ret;
+}
+
+bool_t a_Utf8_combining_char(int unicode)
+{
+ return ((unicode >= 0x0300 && unicode <= 0x036f) ||
+ (unicode >= 0x1dc0 && unicode <= 0x1dff) ||
+ (unicode >= 0x20d0 && unicode <= 0x20ff) ||
+ (unicode >= 0xfe20 && unicode <= 0xfe2f));
+}
diff --git a/src/utf8.hh b/src/utf8.hh
new file mode 100644
index 00000000..4ded50b8
--- /dev/null
+++ b/src/utf8.hh
@@ -0,0 +1,33 @@
+#ifndef __UTF8_HH__
+#define __UTF8_HH__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#include "d_size.h"
+
+/*
+ * Unicode replacement character U+FFFD
+ * "used to replace an incoming character whose value is unknown or otherwise
+ * unrepresentable in Unicode"
+ */
+static const char utf8_replacement_char[] = "\xEF\xBF\xBD";
+
+/* Unicode zero width space U+200B */
+static const char utf8_zero_width_space[] = "\xE2\x80\x8B";
+
+uint_t a_Utf8_end_of_char(const char *str, uint_t i);
+uint_t a_Utf8_decode(const char*, const char* end, int* len);
+int a_Utf8_encode(unsigned int ucs, char *buf);
+int a_Utf8_test(const char* src, unsigned int srclen);
+bool_t a_Utf8_ideographic(const char *s, const char *end, int *len);
+bool_t a_Utf8_combining_char(int unicode);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __UTF8_HH__ */
+
diff --git a/src/web.cc b/src/web.cc
index b9141913..17302b1c 100644
--- a/src/web.cc
+++ b/src/web.cc
@@ -9,10 +9,6 @@
* (at your option) any later version.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h> /* for rint */
-
#include "msg.h"
#include "nav.h"
@@ -22,7 +18,7 @@
#include "IO/mime.h"
#include "dw/core.hh"
-#include "prefs.h"
+#include "styleengine.hh"
#include "web.hh"
// Platform independent part
@@ -54,9 +50,6 @@ int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web,
CA_Callback_t *Call, void **Data)
{
Widget *dw = NULL;
- style::StyleAttrs styleAttrs;
- style::Style *widgetStyle;
- style::FontAttrs fontAttrs;
_MSG("a_Web_dispatch_by_type\n");
@@ -67,25 +60,17 @@ int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web,
if (Web->flags & WEB_RootUrl) {
/* We have RootUrl! */
+
+ /* Set a style for the widget */
+ StyleEngine styleEngine (layout);
+ styleEngine.startElement ("body");
+ Web->bgColor= styleEngine.backgroundStyle()->backgroundColor->getColor();
+
dw = (Widget*) a_Mime_set_viewer(Type, Web, Call, Data);
if (dw == NULL)
return -1;
- /* Set a style for the widget */
- fontAttrs.name = prefs.vw_fontname;
- fontAttrs.size = (int) rint(14.0 * prefs.font_factor);
- fontAttrs.weight = 400;
- fontAttrs.style = style::FONT_STYLE_NORMAL;
-
- styleAttrs.initValues ();
- styleAttrs.margin.setVal (5);
- styleAttrs.font = style::Font::create (layout, &fontAttrs);
- styleAttrs.color = style::Color::createSimple (layout, 0xff0000);
- styleAttrs.backgroundColor =
- style::Color::createSimple (layout, prefs.bg_color);
- widgetStyle = style::Style::create (layout, &styleAttrs);
- dw->setStyle (widgetStyle);
- widgetStyle->unref ();
+ dw->setStyle (styleEngine.style ());
/* This method frees the old dw if any */
layout->setWidget(dw);
@@ -118,19 +103,21 @@ int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web,
/*
* Allocate and set safe values for a DilloWeb structure
*/
-DilloWeb* a_Web_new(const DilloUrl *url)
+DilloWeb* a_Web_new(const DilloUrl *url, const DilloUrl *requester)
{
DilloWeb *web= dNew(DilloWeb, 1);
_MSG(" a_Web_new: ValidWebs ==> %d\n", dList_length(ValidWebs));
web->url = a_Url_dup(url);
+ web->requester = a_Url_dup(requester);
web->bw = NULL;
web->flags = 0;
web->Image = NULL;
web->filename = NULL;
web->stream = NULL;
web->SavedBytes = 0;
-
+ web->bgColor = 0x000000; /* Dummy value will be overwritten
+ * in a_Web_dispatch_by_type. */
dList_append(ValidWebs, (void *)web);
return web;
}
@@ -149,12 +136,12 @@ int a_Web_valid(DilloWeb *web)
void a_Web_free(DilloWeb *web)
{
if (!web) return;
- if (web->url)
- a_Url_free(web->url);
- if (web->Image)
- a_Image_unref(web->Image);
+ a_Url_free(web->url);
+ a_Url_free(web->requester);
+ a_Image_unref(web->Image);
dFree(web->filename);
dList_remove(ValidWebs, (void *)web);
+ _MSG("a_Web_free: ValidWebs=%d\n", dList_length(ValidWebs));
dFree(web);
}
diff --git a/src/web.hh b/src/web.hh
index a5e05a2f..d017903a 100644
--- a/src/web.hh
+++ b/src/web.hh
@@ -22,18 +22,21 @@ typedef struct _DilloWeb DilloWeb;
struct _DilloWeb {
DilloUrl *url; /* Requested URL */
+ DilloUrl *requester; /* URL that caused this request, or
+ * NULL if user-initiated. */
BrowserWindow *bw; /* The requesting browser window [reference] */
int flags; /* Additional info */
DilloImage *Image; /* For image urls [reference] */
+ int32_t bgColor; /* for image backgrounds */
char *filename; /* Variables for Local saving */
FILE *stream;
int SavedBytes;
};
void a_Web_init(void);
-DilloWeb* a_Web_new (const DilloUrl* url);
+DilloWeb* a_Web_new (const DilloUrl* url, const DilloUrl *requester);
int a_Web_valid(DilloWeb *web);
void a_Web_free (DilloWeb*);
int a_Web_dispatch_by_type (const char *Type, DilloWeb *web,
diff --git a/src/xembed.cc b/src/xembed.cc
new file mode 100644
index 00000000..04a4362a
--- /dev/null
+++ b/src/xembed.cc
@@ -0,0 +1,165 @@
+/*
+ * File: xembed.cc
+ *
+ * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <ctype.h>
+
+#include <fltk/Window.h>
+#include <fltk/run.h>
+#include <fltk/events.h>
+#include <fltk/x.h>
+
+#include "xembed.hh"
+
+#if USE_X11
+
+typedef enum {
+ XEMBED_EMBEDDED_NOTIFY = 0,
+ XEMBED_WINDOW_ACTIVATE = 1,
+ XEMBED_WINDOW_DEACTIVATE = 2,
+ XEMBED_REQUEST_FOCUS = 3,
+ XEMBED_FOCUS_IN = 4,
+ XEMBED_FOCUS_OUT = 5,
+ XEMBED_FOCUS_NEXT = 6,
+ XEMBED_FOCUS_PREV = 7,
+ XEMBED_GRAB_KEY = 8,
+ XEMBED_UNGRAB_KEY = 9,
+ XEMBED_MODALITY_ON = 10,
+ XEMBED_MODALITY_OFF = 11,
+} XEmbedMessageType;
+
+void
+Xembed::setXembedInfo(unsigned long flags)
+{
+ unsigned long buffer[2];
+
+ Atom xembed_info_atom = XInternAtom (fltk::xdisplay, "_XEMBED_INFO", false);
+
+ buffer[0] = 1;
+ buffer[1] = flags;
+
+ XChangeProperty (fltk::xdisplay,
+ xid,
+ xembed_info_atom, xembed_info_atom, 32,
+ PropModeReplace,
+ (unsigned char *)buffer, 2);
+}
+
+void
+Xembed::sendXembedEvent(uint32_t message) {
+ XClientMessageEvent xclient;
+
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.window = xid;
+ xclient.type = ClientMessage;
+ xclient.message_type = XInternAtom (fltk::xdisplay, "_XEMBED", false);
+ xclient.format = 32;
+ xclient.data.l[0] = fltk::event_time;
+ xclient.data.l[1] = message;
+
+ XSendEvent(fltk::xdisplay, xid, False, NoEventMask, (XEvent *)&xclient);
+ XSync(fltk::xdisplay, False);
+}
+
+int
+Xembed::handle(int e) {
+ if (e == fltk::PUSH)
+ sendXembedEvent(XEMBED_REQUEST_FOCUS);
+
+ return Window::handle(e);
+}
+
+static int event_handler(int e, fltk::Window *w) {
+ Atom xembed_atom = XInternAtom (fltk::xdisplay, "_XEMBED", false);
+
+ if (fltk::xevent.type == ClientMessage) {
+ if (fltk::xevent.xclient.message_type == xembed_atom) {
+ long message = fltk::xevent.xclient.data.l[1];
+
+ switch (message) {
+ case XEMBED_WINDOW_ACTIVATE:
+ // Force a ConfigureNotify message so fltk can get the new
+ // coordinates after a move of the embedder window.
+ w->resize(0, 0, w->w(), w->h());
+ break;
+ case XEMBED_WINDOW_DEACTIVATE:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// TODO: Implement more XEMBED support;
+
+void Xembed::create() {
+ createInternal(xid);
+ setXembedInfo(1);
+ fltk::add_event_handler(event_handler);
+}
+
+void Xembed::createInternal(uint32_t parent) {
+ fltk::Window *window = this;
+ Colormap colormap = fltk::xcolormap;
+
+ XSetWindowAttributes attr;
+ attr.border_pixel = 0;
+ attr.colormap = colormap;
+ attr.bit_gravity = 0; // StaticGravity;
+ int mask = CWBorderPixel|CWColormap|CWEventMask|CWBitGravity;
+
+ int W = window->w();
+ if (W <= 0) W = 1; // X don't like zero...
+ int H = window->h();
+ if (H <= 0) H = 1; // X don't like zero...
+ int X = window->x();
+ int Y = window->y();
+
+ attr.event_mask =
+ ExposureMask | StructureNotifyMask
+ | KeyPressMask | KeyReleaseMask | KeymapStateMask | FocusChangeMask
+ | ButtonPressMask | ButtonReleaseMask
+ | EnterWindowMask | LeaveWindowMask
+ | PointerMotionMask;
+
+ fltk::CreatedWindow::set_xid(window,
+ XCreateWindow(fltk::xdisplay,
+ parent,
+ X, Y, W, H,
+ 0, // borderwidth
+ fltk::xvisual->depth,
+ InputOutput,
+ fltk::xvisual->visual,
+ mask, &attr));
+}
+
+#else // USE_X11
+
+void
+Xembed::setXembedInfo(unsigned long flags) {};
+
+void
+Xembed::sendXembedEvent(uint32_t message) {};
+
+int
+Xembed::handle(int e) {
+ return Window::handle(e);
+}
+
+void
+Xembed::create() {
+ Window::create();
+}
+
+#endif
diff --git a/src/xembed.hh b/src/xembed.hh
new file mode 100644
index 00000000..70d79c5f
--- /dev/null
+++ b/src/xembed.hh
@@ -0,0 +1,23 @@
+#ifndef __XEMBED_HH__
+#define __XEMBED_HH__
+
+#include <fltk/Window.h>
+
+#include "d_size.h"
+
+class Xembed : public fltk::Window {
+ private:
+ uint32_t xid;
+ void createInternal(uint32_t parent);
+ void setXembedInfo(unsigned long flags);
+ void sendXembedEvent(uint32_t message);
+
+ public:
+ Xembed(uint32_t xid, int _w, int _h) : fltk::Window(_w, _h) {
+ this->xid = xid;
+ };
+ void create();
+ int handle(int event);
+};
+
+#endif