PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/html/gnc-html-webkit.c

http://github.com/mchochlov/Gnucash
C | 1121 lines | 809 code | 173 blank | 139 comment | 109 complexity | 21fe478d9f32b606fd8f954eee274c16 MD5 | raw file
Possible License(s): GPL-2.0
  1. /********************************************************************
  2. * gnc-html-webkit.c -- gnucash report renderer using webkit *
  3. * *
  4. * Copyright (C) 2000 Bill Gribble <grib@billgribble.com> *
  5. * Copyright (C) 2001 Linas Vepstas <linas@linas.org> *
  6. * Copyright (C) 2009 Phil Longstaff <plongstaff@rogers.com> *
  7. * *
  8. * This program is free software; you can redistribute it and/or *
  9. * modify it under the terms of the GNU General Public License as *
  10. * published by the Free Software Foundation; either version 2 of *
  11. * the License, or (at your option) any later version. *
  12. * *
  13. * This program is distributed in the hope that it will be useful, *
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  16. * GNU General Public License for more details. *
  17. * *
  18. * You should have received a copy of the GNU General Public License*
  19. * along with this program; if not, contact: *
  20. * *
  21. * Free Software Foundation Voice: +1-617-542-5942 *
  22. * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
  23. * Boston, MA 02110-1301, USA gnu@gnu.org *
  24. ********************************************************************/
  25. #include "config.h"
  26. #include "platform.h"
  27. #include <gtk/gtk.h>
  28. #include <glib/gi18n.h>
  29. #include <glib/gstdio.h>
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <errno.h>
  35. #include <fcntl.h>
  36. #include <unistd.h>
  37. #include <regex.h>
  38. #include <libguile.h>
  39. #include <webkit/webkit.h>
  40. #include "Account.h"
  41. #include "gnc-gui-query.h"
  42. #include "gnc-engine.h"
  43. #include "gnc-html.h"
  44. #include "gnc-html-webkit.h"
  45. #include "gnc-html-history.h"
  46. #include "gnc-html-graph-gog-webkit.h"
  47. #include "print-session.h"
  48. G_DEFINE_TYPE(GncHtmlWebkit, gnc_html_webkit, GNC_TYPE_HTML )
  49. static void gnc_html_webkit_dispose( GObject* obj );
  50. static void gnc_html_webkit_finalize( GObject* obj );
  51. static void gnc_html_webkit_class_init( GncHtmlWebkitClass* klass );
  52. static void gnc_html_webkit_init( GncHtmlWebkit* gs );
  53. #define GNC_HTML_WEBKIT_GET_PRIVATE(o) (GNC_HTML_WEBKIT(o)->priv)
  54. #include "gnc-html-webkit-p.h"
  55. /* indicates the debugging module that this .o belongs to. */
  56. static QofLogModule log_module = GNC_MOD_HTML;
  57. /* hashes for URLType -> protocol and protocol -> URLType */
  58. //extern GHashTable* gnc_html_type_to_proto_hash;
  59. extern GHashTable* gnc_html_proto_to_type_hash;
  60. /* hashes an HTML <object classid="ID"> classid to a handler function */
  61. extern GHashTable* gnc_html_object_handlers;
  62. /* hashes handlers for loading different URLType data */
  63. extern GHashTable* gnc_html_stream_handlers;
  64. /* hashes handlers for handling different URLType data */
  65. extern GHashTable* gnc_html_url_handlers;
  66. static char error_404_format[] = "<html><body><h3>%s</h3><p>%s</body></html>";
  67. static char error_404_title[] = N_("Not found");
  68. static char error_404_body[] = N_("The specified URL could not be loaded.");
  69. #define BASE_URI_NAME "base-uri"
  70. static WebKitNavigationResponse webkit_navigation_requested_cb(
  71. WebKitWebView* web_view,
  72. WebKitWebFrame* frame,
  73. WebKitNetworkRequest* request,
  74. gpointer user_data );
  75. static void webkit_on_url_cb( WebKitWebView* web_view, gchar* title, gchar* url,
  76. gpointer data );
  77. static gchar* handle_embedded_object( GncHtmlWebkit* self, gchar* html_str );
  78. #if 0
  79. static void gnc_html_set_base_cb( GtkHTML* gtkhtml, const gchar* base, gpointer data );
  80. static void gnc_html_link_clicked_cb( GtkHTML* html, const gchar* url, gpointer data );
  81. static gboolean gnc_html_object_requested_cb( GtkHTML* html, GtkHTMLEmbedded* eb,
  82. gpointer data );
  83. #endif
  84. static int gnc_html_button_press_cb( GtkWidget* widg, GdkEventButton* event,
  85. gpointer user_data );
  86. static void impl_webkit_show_url( GncHtml* self, URLType type,
  87. const gchar* location, const gchar* label,
  88. gboolean new_window_hint );
  89. static void impl_webkit_show_data( GncHtml* self, const gchar* data, int datalen );
  90. static void impl_webkit_reload( GncHtml* self );
  91. static void impl_webkit_copy_to_clipboard( GncHtml* self );
  92. static gboolean impl_webkit_export_to_file( GncHtml* self, const gchar* filepath );
  93. static void impl_webkit_print( GncHtml* self, const gchar* jobname );
  94. static void impl_webkit_cancel( GncHtml* self );
  95. static void impl_webkit_set_parent( GncHtml* self, GtkWindow* parent );
  96. static void
  97. gnc_html_webkit_init( GncHtmlWebkit* self )
  98. {
  99. GncHtmlWebkitPrivate* priv;
  100. GncHtmlWebkitPrivate* new_priv;
  101. WebKitWebSettings* webkit_settings = NULL;
  102. const char* default_font_family = NULL;
  103. new_priv = g_realloc( GNC_HTML(self)->priv, sizeof(GncHtmlWebkitPrivate) );
  104. priv = self->priv = new_priv;
  105. GNC_HTML(self)->priv = (GncHtmlPrivate*)priv;
  106. priv->html_string = NULL;
  107. priv->web_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
  108. /* Get the default font family from GtkStyle of a GtkWidget(priv-web_view). */
  109. default_font_family = pango_font_description_get_family( gtk_rc_get_style(GTK_WIDGET(priv->web_view))->font_desc );
  110. /* Set default webkit settings */
  111. webkit_settings = webkit_web_view_get_settings (priv->web_view);
  112. g_object_set (G_OBJECT(webkit_settings), "default-encoding", "utf-8", NULL);
  113. if (default_font_family == NULL)
  114. {
  115. PWARN("webkit_settings: Cannot get default font family.");
  116. }
  117. else
  118. {
  119. g_object_set (G_OBJECT(webkit_settings),
  120. "default-font-family", default_font_family,
  121. NULL);
  122. PINFO("webkit_settings: Set default font to [%s]", default_font_family);
  123. }
  124. gtk_container_add( GTK_CONTAINER(priv->base.container),
  125. GTK_WIDGET(priv->web_view) );
  126. g_object_ref_sink( priv->base.container );
  127. /* signals */
  128. g_signal_connect( priv->web_view, "navigation-requested",
  129. G_CALLBACK(webkit_navigation_requested_cb),
  130. self);
  131. g_signal_connect( priv->web_view, "hovering-over-link",
  132. G_CALLBACK(webkit_on_url_cb),
  133. self );
  134. #if 0
  135. g_signal_connect( priv->html, "set_base",
  136. G_CALLBACK(gnc_html_set_base_cb),
  137. self);
  138. g_signal_connect(priv->html, "link_clicked",
  139. G_CALLBACK(gnc_html_link_clicked_cb),
  140. self);
  141. g_signal_connect (priv->html, "object_requested",
  142. G_CALLBACK (gnc_html_object_requested_cb),
  143. self);
  144. g_signal_connect (priv->html, "button_press_event",
  145. G_CALLBACK (gnc_html_button_press_cb),
  146. self);
  147. g_signal_connect (priv->html, "submit",
  148. G_CALLBACK(gnc_html_submit_cb),
  149. self);
  150. #endif
  151. LEAVE("retval %p", self);
  152. }
  153. static void
  154. gnc_html_webkit_class_init( GncHtmlWebkitClass* klass )
  155. {
  156. GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
  157. GncHtmlClass* html_class = GNC_HTML_CLASS(klass);
  158. gobject_class->dispose = gnc_html_webkit_dispose;
  159. gobject_class->finalize = gnc_html_webkit_finalize;
  160. html_class->show_url = impl_webkit_show_url;
  161. html_class->show_data = impl_webkit_show_data;
  162. html_class->reload = impl_webkit_reload;
  163. html_class->copy_to_clipboard = impl_webkit_copy_to_clipboard;
  164. html_class->export_to_file = impl_webkit_export_to_file;
  165. html_class->print = impl_webkit_print;
  166. html_class->cancel = impl_webkit_cancel;
  167. html_class->set_parent = impl_webkit_set_parent;
  168. // Initialize graphing support
  169. gnc_html_graph_gog_webkit_init();
  170. }
  171. static void
  172. gnc_html_webkit_dispose( GObject* obj )
  173. {
  174. GncHtmlWebkit* self = GNC_HTML_WEBKIT(obj);
  175. GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  176. if ( priv->web_view != NULL )
  177. {
  178. gtk_container_remove( GTK_CONTAINER(priv->base.container),
  179. GTK_WIDGET(priv->web_view) );
  180. priv->web_view = NULL;
  181. }
  182. if ( priv->html_string != NULL )
  183. {
  184. g_free( priv->html_string );
  185. priv->html_string = NULL;
  186. }
  187. G_OBJECT_CLASS(gnc_html_webkit_parent_class)->dispose( obj );
  188. }
  189. static void
  190. gnc_html_webkit_finalize( GObject* obj )
  191. {
  192. GncHtmlWebkit* self = GNC_HTML_WEBKIT(obj);
  193. // if( self->priv != NULL ) {
  194. // g_free( self->priv );
  195. self->priv = NULL;
  196. // }
  197. G_OBJECT_CLASS(gnc_html_webkit_parent_class)->finalize( obj );
  198. }
  199. /*****************************************************************************/
  200. static char*
  201. extract_base_name(URLType type, const gchar* path)
  202. {
  203. gchar machine_rexp[] = "^(//[^/]*)/*(/.*)?$";
  204. gchar path_rexp[] = "^/*(.*)/+([^/]*)$";
  205. regex_t compiled_m, compiled_p;
  206. regmatch_t match[4];
  207. gchar * machine = NULL, * location = NULL, * base = NULL;
  208. gchar * basename = NULL;
  209. DEBUG(" ");
  210. if (!path) return NULL;
  211. regcomp(&compiled_m, machine_rexp, REG_EXTENDED);
  212. regcomp(&compiled_p, path_rexp, REG_EXTENDED);
  213. if (!safe_strcmp (type, URL_TYPE_HTTP) ||
  214. !safe_strcmp (type, URL_TYPE_SECURE) ||
  215. !safe_strcmp (type, URL_TYPE_FTP))
  216. {
  217. /* step 1: split the machine name away from the path
  218. * components */
  219. if (!regexec(&compiled_m, path, 4, match, 0))
  220. {
  221. /* $1 is the machine name */
  222. if (match[1].rm_so != -1)
  223. {
  224. machine = g_strndup(path + match[1].rm_so,
  225. match[1].rm_eo - match[1].rm_so);
  226. }
  227. /* $2 is the path */
  228. if (match[2].rm_so != -1)
  229. {
  230. location = g_strndup(path + match[2].rm_so,
  231. match[2].rm_eo - match[2].rm_so);
  232. }
  233. }
  234. }
  235. else
  236. {
  237. location = g_strdup(path);
  238. }
  239. /* step 2: split up the path into prefix and file components */
  240. if (location)
  241. {
  242. if (!regexec(&compiled_p, location, 4, match, 0))
  243. {
  244. if (match[1].rm_so != -1)
  245. {
  246. base = g_strndup(location + match[1].rm_so,
  247. match[1].rm_eo - match[1].rm_so);
  248. }
  249. else
  250. {
  251. base = NULL;
  252. }
  253. }
  254. }
  255. regfree(&compiled_m);
  256. regfree(&compiled_p);
  257. if (machine)
  258. {
  259. if (base && (strlen(base) > 0))
  260. {
  261. basename = g_strconcat(machine, "/", base, "/", NULL);
  262. }
  263. else
  264. {
  265. basename = g_strconcat(machine, "/", NULL);
  266. }
  267. }
  268. else
  269. {
  270. if (base && (strlen(base) > 0))
  271. {
  272. basename = g_strdup(base);
  273. }
  274. else
  275. {
  276. basename = NULL;
  277. }
  278. }
  279. g_free(machine);
  280. g_free(base);
  281. g_free(location);
  282. return basename;
  283. }
  284. static gboolean
  285. http_allowed()
  286. {
  287. return TRUE;
  288. }
  289. static gboolean
  290. https_allowed()
  291. {
  292. return TRUE;
  293. }
  294. static gchar*
  295. handle_embedded_object( GncHtmlWebkit* self, gchar* html_str )
  296. {
  297. // Find the <object> tag and get the classid from it. This will provide the correct
  298. // object callback handler. Pass the <object> entity text to the handler. What should
  299. // come back is embedded image information.
  300. gchar* object_tag;
  301. gchar* end_object_tag;
  302. gchar* object_contents;
  303. gchar* html_str_start;
  304. gchar* html_str_middle;
  305. gchar* html_str_result;
  306. gchar* classid;
  307. gchar* classid_end;
  308. gchar* object_classid;
  309. GncHTMLObjectCB h;
  310. object_tag = g_strstr_len( html_str, -1, "<object classid=" );
  311. if ( object_tag == NULL )
  312. {
  313. // Hmmm... no object tag
  314. return html_str;
  315. }
  316. classid = object_tag + strlen( "<object classid=" ) + 1;
  317. classid_end = g_strstr_len( classid, -1, "\"" );
  318. object_classid = g_strndup( classid, (classid_end - classid) );
  319. end_object_tag = g_strstr_len( object_tag, -1, "</object>" );
  320. if ( end_object_tag == NULL )
  321. {
  322. // Hmmm... no object end tag
  323. return html_str;
  324. }
  325. end_object_tag += strlen( "</object>" );
  326. object_contents = g_strndup( object_tag, (end_object_tag - object_tag) );
  327. h = g_hash_table_lookup( gnc_html_object_handlers, object_classid );
  328. if ( h != NULL )
  329. {
  330. (void)h( GNC_HTML(self), object_contents, &html_str_middle );
  331. }
  332. else
  333. {
  334. html_str_middle = g_strdup_printf( "No handler found for classid \"%s\"", object_classid );
  335. }
  336. html_str_start = g_strndup( html_str, (object_tag - html_str) );
  337. html_str_result = g_strdup_printf( "%s%s%s", html_str_start, html_str_middle, end_object_tag );
  338. g_free( html_str_start );
  339. g_free( html_str_middle );
  340. return html_str_result;
  341. }
  342. /********************************************************************
  343. * load_to_stream : actually do the work of loading the HTML
  344. * or binary data referenced by a URL and feeding it into the webkit
  345. * widget.
  346. ********************************************************************/
  347. static void
  348. load_to_stream( GncHtmlWebkit* self, URLType type,
  349. const gchar* location, const gchar* label )
  350. {
  351. gchar* fdata = NULL;
  352. int fdata_len = 0;
  353. GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  354. DEBUG( "type %s, location %s, label %s", type ? type : "(null)",
  355. location ? location : "(null)", label ? label : "(null)");
  356. g_return_if_fail( self != NULL );
  357. if ( gnc_html_stream_handlers != NULL )
  358. {
  359. GncHTMLStreamCB stream_handler;
  360. stream_handler = g_hash_table_lookup( gnc_html_stream_handlers, type );
  361. if ( stream_handler )
  362. {
  363. gboolean ok = stream_handler( location, &fdata, &fdata_len );
  364. if ( ok )
  365. {
  366. fdata = fdata ? fdata : g_strdup( "" );
  367. // Until webkitgtk supports download requests, look for "<object classid="
  368. // indicating the beginning of an embedded graph. If found, handle it
  369. if ( g_strstr_len( fdata, -1, "<object classid=" ) != NULL )
  370. {
  371. gchar* new_fdata;
  372. new_fdata = handle_embedded_object( self, fdata );
  373. g_free( fdata );
  374. fdata = new_fdata;
  375. }
  376. // Save a copy for export purposes
  377. if ( priv->html_string != NULL )
  378. {
  379. g_free( priv->html_string );
  380. }
  381. priv->html_string = g_strdup( fdata );
  382. impl_webkit_show_data( GNC_HTML(self), fdata, strlen(fdata) );
  383. // webkit_web_view_load_html_string( priv->web_view, fdata, BASE_URI_NAME );
  384. }
  385. else
  386. {
  387. fdata = fdata ? fdata :
  388. g_strdup_printf( error_404_format,
  389. _(error_404_title), _(error_404_body) );
  390. webkit_web_view_load_html_string( priv->web_view, fdata, BASE_URI_NAME );
  391. }
  392. g_free( fdata );
  393. if ( label )
  394. {
  395. while ( gtk_events_pending() )
  396. {
  397. gtk_main_iteration();
  398. }
  399. /* No action required: Webkit jumps to the anchor on its own. */
  400. }
  401. return;
  402. }
  403. }
  404. do
  405. {
  406. if ( !safe_strcmp( type, URL_TYPE_SECURE ) ||
  407. !safe_strcmp( type, URL_TYPE_HTTP ) )
  408. {
  409. if ( !safe_strcmp( type, URL_TYPE_SECURE ) )
  410. {
  411. if ( !https_allowed() )
  412. {
  413. gnc_error_dialog( priv->base.parent, "%s",
  414. _("Secure HTTP access is disabled. "
  415. "You can enable it in the Network section of "
  416. "the Preferences dialog."));
  417. break;
  418. }
  419. }
  420. if ( !http_allowed() )
  421. {
  422. gnc_error_dialog( priv->base.parent, "%s",
  423. _("Network HTTP access is disabled. "
  424. "You can enable it in the Network section of "
  425. "the Preferences dialog."));
  426. }
  427. else
  428. {
  429. char *fullurl;
  430. fullurl = gnc_build_url( type, location, label );
  431. }
  432. }
  433. else
  434. {
  435. PWARN( "load_to_stream for inappropriate type\n"
  436. "\turl = '%s#%s'\n",
  437. location ? location : "(null)",
  438. label ? label : "(null)" );
  439. fdata = g_strdup_printf( error_404_format,
  440. _(error_404_title), _(error_404_body) );
  441. webkit_web_view_load_html_string( priv->web_view, fdata, BASE_URI_NAME );
  442. g_free( fdata );
  443. }
  444. }
  445. while ( FALSE );
  446. }
  447. #if 0
  448. /********************************************************************
  449. * gnc_html_link_clicked_cb - called when user left-clicks on html
  450. * anchor.
  451. ********************************************************************/
  452. static void
  453. gnc_html_link_clicked_cb( GtkHTML* html, const gchar* url, gpointer data )
  454. {
  455. URLType type;
  456. gchar* location = NULL;
  457. gchar* label = NULL;
  458. GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
  459. DEBUG("Clicked %s", url);
  460. type = gnc_html_parse_url( GNC_HTML(self), url, &location, &label );
  461. gnc_html_show_url( GNC_HTML(self), type, location, label, 0 );
  462. g_free( location );
  463. g_free( label );
  464. }
  465. #endif
  466. /********************************************************************
  467. * webkit_navigation_requested_cb - called when a URL needs to be
  468. * loaded within the loading of a page (embedded image).
  469. ********************************************************************/
  470. static WebKitNavigationResponse
  471. webkit_navigation_requested_cb( WebKitWebView* web_view, WebKitWebFrame* frame,
  472. WebKitNetworkRequest* request,
  473. gpointer data )
  474. {
  475. URLType type;
  476. gchar* location = NULL;
  477. gchar* label = NULL;
  478. GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
  479. const gchar* url = webkit_network_request_get_uri( request );
  480. ENTER( "requesting %s", url );
  481. if ( strcmp( url, BASE_URI_NAME ) == 0 )
  482. {
  483. LEAVE("URI is %s", BASE_URI_NAME);
  484. return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
  485. }
  486. type = gnc_html_parse_url( GNC_HTML(self), url, &location, &label );
  487. if ( strcmp( type, "file" ) == 0 )
  488. {
  489. LEAVE("URI type is 'file'");
  490. return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
  491. }
  492. gnc_html_show_url( GNC_HTML(self), type, location, label, 0 );
  493. // load_to_stream( self, type, location, label );
  494. g_free( location );
  495. g_free( label );
  496. LEAVE("");
  497. return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
  498. }
  499. #if 0
  500. /********************************************************************
  501. * gnc_html_object_requested_cb - called when an applet needs to be
  502. * loaded.
  503. ********************************************************************/
  504. static gboolean
  505. gnc_html_object_requested_cb( GtkHTML* html, GtkHTMLEmbedded* eb,
  506. gpointer data )
  507. {
  508. GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
  509. GncHTMLObjectCB h;
  510. DEBUG( " " );
  511. if ( !eb || !(eb->classid) || !gnc_html_object_handlers ) return FALSE;
  512. h = g_hash_table_lookup( gnc_html_object_handlers, eb->classid );
  513. if ( h )
  514. {
  515. return h( GNC_HTML(self), eb, data );
  516. }
  517. else
  518. {
  519. return FALSE;
  520. }
  521. }
  522. #endif
  523. /********************************************************************
  524. * webkit_on_url_cb - called when user rolls over html anchor
  525. ********************************************************************/
  526. static void
  527. webkit_on_url_cb( WebKitWebView* web_view, gchar* title, gchar* url, gpointer data )
  528. {
  529. GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
  530. GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  531. DEBUG( "Rollover %s", url ? url : "(null)" );
  532. g_free( priv->base.current_link );
  533. priv->base.current_link = g_strdup( url );
  534. if ( priv->base.flyover_cb )
  535. {
  536. (priv->base.flyover_cb)( GNC_HTML(self), url, priv->base.flyover_cb_data );
  537. }
  538. }
  539. #if 0
  540. /********************************************************************
  541. * gnc_html_set_base_cb
  542. ********************************************************************/
  543. static void
  544. gnc_html_set_base_cb( GtkHTML* gtkhtml, const gchar* base,
  545. gpointer data )
  546. {
  547. GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
  548. GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  549. URLType type;
  550. gchar* location = NULL;
  551. gchar* label = NULL;
  552. DEBUG( "Setting base location to %s", base );
  553. type = gnc_html_parse_url( GNC_HTML(self), base, &location, &label );
  554. g_free( priv->base.base_location );
  555. g_free( label );
  556. priv->base.base_type = type;
  557. priv->base.base_location = location;
  558. }
  559. #endif
  560. /********************************************************************
  561. * gnc_html_button_press_cb
  562. * mouse button callback (if any)
  563. ********************************************************************/
  564. static int
  565. gnc_html_button_press_cb( GtkWidget* widg, GdkEventButton* event,
  566. gpointer user_data )
  567. {
  568. GncHtmlWebkit* self = GNC_HTML_WEBKIT(user_data);
  569. GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  570. DEBUG( "Button Press" );
  571. if ( priv->base.button_cb != NULL )
  572. {
  573. (priv->base.button_cb)( GNC_HTML(self), event, priv->base.button_cb_data );
  574. return TRUE;
  575. }
  576. else
  577. {
  578. return FALSE;
  579. }
  580. }
  581. /********************************************************************
  582. * gnc_html_open_scm
  583. * insert some scheme-generated HTML
  584. ********************************************************************/
  585. static void
  586. gnc_html_open_scm( GncHtmlWebkit* self, const gchar * location,
  587. const gchar * label, int newwin )
  588. {
  589. PINFO("location='%s'", location ? location : "(null)");
  590. }
  591. /********************************************************************
  592. * gnc_html_show_data
  593. * display some HTML that the creator of the gnc-html got from
  594. * somewhere.
  595. ********************************************************************/
  596. static void
  597. impl_webkit_show_data( GncHtml* self, const gchar* data, int datalen )
  598. {
  599. GncHtmlWebkitPrivate* priv;
  600. #if HAVE(WEBKIT_WEB_VIEW_LOAD_URI)
  601. #define TEMPLATE_REPORT_FILE_NAME "gnc-report-XXXXXX.html"
  602. int fd;
  603. gchar* uri;
  604. gchar *filename;
  605. #endif
  606. g_return_if_fail( self != NULL );
  607. g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
  608. ENTER( "datalen %d, data %20.20s", datalen, data );
  609. priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  610. #if HAVE(WEBKIT_WEB_VIEW_LOAD_URI)
  611. /* Export the HTML to a file and load the file URI. On Linux, this seems to get around some
  612. security problems (otherwise, it can complain that embedded images aren't permitted to be
  613. viewed because they are local resources). On Windows, this allows the embedded images to
  614. be viewed (maybe for the same reason as on Linux, but I haven't found where it puts those
  615. messages. */
  616. filename = g_build_filename(g_get_tmp_dir(), TEMPLATE_REPORT_FILE_NAME, (gchar *)NULL);
  617. fd = g_mkstemp( filename );
  618. impl_webkit_export_to_file( self, filename );
  619. close( fd );
  620. uri = g_strdup_printf( "file:///%s", filename );
  621. g_free(filename);
  622. DEBUG("Loading uri '%s'", uri);
  623. webkit_web_view_load_uri( priv->web_view, uri );
  624. g_free( uri );
  625. #else
  626. webkit_web_view_load_html_string( priv->web_view, data, BASE_URI_NAME );
  627. #endif
  628. LEAVE("");
  629. }
  630. /********************************************************************
  631. * gnc_html_show_url
  632. *
  633. * open a URL. This is called when the user clicks a link or
  634. * for the creator of the gnc_html window to explicitly request
  635. * a URL.
  636. ********************************************************************/
  637. static void
  638. impl_webkit_show_url( GncHtml* self, URLType type,
  639. const gchar* location, const gchar* label,
  640. gboolean new_window_hint )
  641. {
  642. GncHTMLUrlCB url_handler;
  643. gboolean new_window;
  644. GncHtmlWebkitPrivate* priv;
  645. g_return_if_fail( self != NULL );
  646. g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
  647. g_return_if_fail( location != NULL );
  648. priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  649. /* make sure it's OK to show this URL type in this window */
  650. if ( new_window_hint == 0 )
  651. {
  652. if ( priv->base.urltype_cb )
  653. {
  654. new_window = !((priv->base.urltype_cb)( type ));
  655. }
  656. else
  657. {
  658. new_window = FALSE;
  659. }
  660. }
  661. else
  662. {
  663. new_window = TRUE;
  664. }
  665. if ( !new_window )
  666. {
  667. gnc_html_cancel( GNC_HTML(self) );
  668. }
  669. if ( gnc_html_url_handlers )
  670. {
  671. url_handler = g_hash_table_lookup( gnc_html_url_handlers, type );
  672. }
  673. else
  674. {
  675. url_handler = NULL;
  676. }
  677. if ( url_handler )
  678. {
  679. GNCURLResult result;
  680. gboolean ok;
  681. result.load_to_stream = FALSE;
  682. result.url_type = type;
  683. result.location = NULL;
  684. result.label = NULL;
  685. result.base_type = URL_TYPE_FILE;
  686. result.base_location = NULL;
  687. result.error_message = NULL;
  688. ok = url_handler( location, label, new_window, &result );
  689. if ( !ok )
  690. {
  691. if ( result.error_message )
  692. {
  693. gnc_error_dialog( priv->base.parent, "%s", result.error_message );
  694. }
  695. else
  696. {
  697. /* %s is a URL (some location somewhere). */
  698. gnc_error_dialog( priv->base.parent, _("There was an error accessing %s."), location );
  699. }
  700. if ( priv->base.load_cb )
  701. {
  702. priv->base.load_cb( GNC_HTML(self), result.url_type,
  703. location, label, priv->base.load_cb_data );
  704. }
  705. }
  706. else if ( result.load_to_stream )
  707. {
  708. gnc_html_history_node *hnode;
  709. const char *new_location;
  710. const char *new_label;
  711. new_location = result.location ? result.location : location;
  712. new_label = result.label ? result.label : label;
  713. hnode = gnc_html_history_node_new( result.url_type, new_location, new_label );
  714. gnc_html_history_append( priv->base.history, hnode );
  715. g_free( priv->base.base_location );
  716. priv->base.base_type = result.base_type;
  717. priv->base.base_location =
  718. g_strdup( extract_base_name( result.base_type, new_location ) );
  719. DEBUG( "resetting base location to %s",
  720. priv->base.base_location ? priv->base.base_location : "(null)" );
  721. load_to_stream( GNC_HTML_WEBKIT(self), result.url_type,
  722. new_location, new_label );
  723. if ( priv->base.load_cb != NULL )
  724. {
  725. priv->base.load_cb( GNC_HTML(self), result.url_type,
  726. new_location, new_label, priv->base.load_cb_data );
  727. }
  728. }
  729. g_free( result.location );
  730. g_free( result.label );
  731. g_free( result.base_location );
  732. g_free( result.error_message );
  733. return;
  734. }
  735. if ( safe_strcmp( type, URL_TYPE_SCHEME ) == 0 )
  736. {
  737. gnc_html_open_scm( GNC_HTML_WEBKIT(self), location, label, new_window );
  738. }
  739. else if ( safe_strcmp( type, URL_TYPE_JUMP ) == 0 )
  740. {
  741. /* Webkit jumps to the anchor on its own */
  742. }
  743. else if ( safe_strcmp( type, URL_TYPE_SECURE ) == 0 ||
  744. safe_strcmp( type, URL_TYPE_HTTP ) == 0 ||
  745. safe_strcmp( type, URL_TYPE_FILE ) == 0 )
  746. {
  747. do
  748. {
  749. if ( safe_strcmp( type, URL_TYPE_SECURE ) == 0 )
  750. {
  751. if ( !https_allowed() )
  752. {
  753. gnc_error_dialog( priv->base.parent, "%s",
  754. _("Secure HTTP access is disabled. "
  755. "You can enable it in the Network section of "
  756. "the Preferences dialog.") );
  757. break;
  758. }
  759. }
  760. if ( safe_strcmp( type, URL_TYPE_HTTP ) == 0 )
  761. {
  762. if ( !http_allowed() )
  763. {
  764. gnc_error_dialog( priv->base.parent, "%s",
  765. _("Network HTTP access is disabled. "
  766. "You can enable it in the Network section of "
  767. "the Preferences dialog.") );
  768. break;
  769. }
  770. }
  771. priv->base.base_type = type;
  772. if ( priv->base.base_location != NULL ) g_free( priv->base.base_location );
  773. priv->base.base_location = extract_base_name( type, location );
  774. /* FIXME : handle new_window = 1 */
  775. gnc_html_history_append( priv->base.history,
  776. gnc_html_history_node_new( type, location, label ) );
  777. load_to_stream( GNC_HTML_WEBKIT(self), type, location, label );
  778. }
  779. while ( FALSE );
  780. }
  781. else
  782. {
  783. PERR( "URLType %s not supported.", type );
  784. }
  785. if ( priv->base.load_cb != NULL )
  786. {
  787. (priv->base.load_cb)( GNC_HTML(self), type, location, label, priv->base.load_cb_data );
  788. }
  789. }
  790. /********************************************************************
  791. * gnc_html_reload
  792. * reload the current page
  793. ********************************************************************/
  794. static void
  795. impl_webkit_reload( GncHtml* self )
  796. {
  797. gnc_html_history_node * n;
  798. GncHtmlWebkitPrivate* priv;
  799. g_return_if_fail( self != NULL );
  800. g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
  801. priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  802. n = gnc_html_history_get_current( priv->base.history );
  803. if ( n != NULL )
  804. {
  805. gnc_html_show_url( self, n->type, n->location, n->label, 0 );
  806. }
  807. }
  808. /********************************************************************
  809. * gnc_html_new
  810. * create and set up a new webkit widget.
  811. ********************************************************************/
  812. GncHtml*
  813. gnc_html_webkit_new( void )
  814. {
  815. GncHtmlWebkit* self = g_object_new( GNC_TYPE_HTML_WEBKIT, NULL );
  816. GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  817. return GNC_HTML(self);
  818. }
  819. /********************************************************************
  820. * gnc_html_cancel
  821. * cancel any outstanding HTML fetch requests.
  822. ********************************************************************/
  823. static gboolean
  824. webkit_cancel_helper(gpointer key, gpointer value, gpointer user_data)
  825. {
  826. g_free(key);
  827. g_list_free((GList *)value);
  828. return TRUE;
  829. }
  830. static void
  831. impl_webkit_cancel( GncHtml* self )
  832. {
  833. GncHtmlWebkitPrivate* priv;
  834. g_return_if_fail( self != NULL );
  835. g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
  836. priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  837. /* remove our own references to requests */
  838. //gnc_http_cancel_requests( priv->http );
  839. g_hash_table_foreach_remove( priv->base.request_info, webkit_cancel_helper, NULL );
  840. }
  841. static void
  842. impl_webkit_copy_to_clipboard( GncHtml* self )
  843. {
  844. GncHtmlWebkitPrivate* priv;
  845. g_return_if_fail( self != NULL );
  846. g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
  847. priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  848. if ( webkit_web_view_can_copy_clipboard( priv->web_view ) )
  849. {
  850. webkit_web_view_copy_clipboard( priv->web_view );
  851. }
  852. }
  853. /**************************************************************
  854. * gnc_html_export_to_file
  855. *
  856. * @param self GncHtmlWebkit object
  857. * @param filepath Where to write the HTML
  858. * @return TRUE if successful, FALSE if unsucessful
  859. **************************************************************/
  860. static gboolean
  861. impl_webkit_export_to_file( GncHtml* self, const char *filepath )
  862. {
  863. FILE *fh;
  864. GncHtmlWebkitPrivate* priv;
  865. g_return_val_if_fail( self != NULL, FALSE );
  866. g_return_val_if_fail( GNC_IS_HTML_WEBKIT(self), FALSE );
  867. g_return_val_if_fail( filepath != NULL, FALSE );
  868. priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  869. if ( priv->html_string == NULL )
  870. {
  871. return FALSE;
  872. }
  873. fh = g_fopen( filepath, "w" );
  874. if ( fh != NULL )
  875. {
  876. gint written;
  877. gint len = strlen( priv->html_string );
  878. written = fwrite( priv->html_string, 1, len, fh );
  879. fclose (fh);
  880. if ( written != len )
  881. {
  882. return FALSE;
  883. }
  884. return TRUE;
  885. }
  886. else
  887. {
  888. return FALSE;
  889. }
  890. }
  891. /**
  892. * Prints the current page.
  893. *
  894. * If printing on WIN32, in order to prevent the font from being tiny, (see bug #591177),
  895. * A GtkPrintOperation object needs to be created so that the unit can be set, and then
  896. * webkit_web_frame_print_full() needs to be called to use that GtkPrintOperation. On
  897. * other platforms (specifically linux - not sure about MacOSX), the version of webkit may
  898. * not contain the function webkit_web_frame_print_full(), so webkit_web_frame_print() is
  899. * called instead (the font size problem doesn't show up on linux).
  900. *
  901. * @param self HTML renderer object
  902. */
  903. static void
  904. impl_webkit_print( GncHtml* self, const gchar* jobname )
  905. {
  906. #if !HAVE(WEBKIT_WEB_FRAME_PRINT_FULL)
  907. extern void webkit_web_frame_print( WebKitWebFrame * frame );
  908. #endif
  909. GncHtmlWebkitPrivate* priv;
  910. WebKitWebFrame* frame;
  911. #if HAVE(WEBKIT_WEB_FRAME_PRINT_FULL)
  912. GtkPrintOperation* op = gtk_print_operation_new();
  913. GError* error = NULL;
  914. #endif
  915. priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  916. frame = webkit_web_view_get_main_frame( priv->web_view );
  917. #if HAVE(WEBKIT_WEB_FRAME_PRINT_FULL)
  918. gnc_print_operation_init( op, jobname );
  919. #ifdef G_OS_WIN32
  920. gtk_print_operation_set_unit( op, GTK_UNIT_POINTS );
  921. #endif
  922. webkit_web_frame_print_full( frame, op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, &error );
  923. g_object_unref( op );
  924. if ( error != NULL )
  925. {
  926. GtkWidget* window = gtk_widget_get_toplevel( GTK_WIDGET(priv->web_view) );
  927. GtkWidget* dialog = gtk_message_dialog_new( GTK_WIDGET_TOPLEVEL(window) ? GTK_WINDOW(window) : NULL,
  928. GTK_DIALOG_DESTROY_WITH_PARENT,
  929. GTK_MESSAGE_ERROR,
  930. GTK_BUTTONS_CLOSE,
  931. "%s", error->message );
  932. g_error_free( error );
  933. g_signal_connect( dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);
  934. gtk_widget_show( dialog );
  935. }
  936. #else
  937. webkit_web_frame_print( frame );
  938. #endif
  939. }
  940. static void
  941. impl_webkit_set_parent( GncHtml* self, GtkWindow* parent )
  942. {
  943. GncHtmlWebkitPrivate* priv;
  944. g_return_if_fail( self != NULL );
  945. g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
  946. priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
  947. priv->base.parent = GTK_WIDGET(parent);
  948. }