Web · Wiki · Activities · Blog · Lists · Chat · Meeting · Bugs · Git · Translate · Archive · People · Donate
1
# Copyright (C) 2006, Red Hat, Inc.
2
# Copyright (C) 2007, One Laptop Per Child
3
# Copyright (C) 2009, Tomeu Vizoso, Simon Schampijer
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19
import os
20
import time
21
import re
22
from gettext import gettext as _
23
24
from gi.repository import GObject
25
from gi.repository import Gtk
26
from gi.repository import Gdk
27
from gi.repository import Pango
28
from gi.repository import WebKit
29
from gi.repository import Soup
30
from gi.repository import GConf
31
32
from sugar3 import env
33
from sugar3.activity import activity
34
from sugar3.graphics import style
35
from sugar3.graphics.icon import Icon
36
37
from widgets import BrowserNotebook
38
from palettes import ContentInvoker
39
from filepicker import FilePicker
40
import globalhistory
41
import downloadmanager
42
from pdfviewer import PDFTabPage
43
44
ZOOM_ORIGINAL = 1.0
45
_ZOOM_AMOUNT = 0.1
46
LIBRARY_PATH = '/usr/share/library-common/index.html'
47
48
_WEB_SCHEMES = ['http', 'https', 'ftp', 'file', 'javascript', 'data',
49
                'about', 'gopher', 'mailto']
50
51
_NON_SEARCH_REGEX = re.compile('''
52
    (^localhost(\\.[^\s]+)?(:\\d+)?(/.*)?$|
53
    ^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]$|
54
    ^::[0-9a-f:]*$|                         # IPv6 literals
55
    ^[0-9a-f:]+:[0-9a-f:]*$|                # IPv6 literals
56
    ^[^\\.\s]+\\.[^\\.\s]+.*$|              # foo.bar...
57
    ^https?://[^/\\.\s]+.*$|
58
    ^about:.*$|
59
    ^data:.*$|
60
    ^file:.*$)
61
    ''', re.VERBOSE)
62
63
DEFAULT_ERROR_PAGE = os.path.join(activity.get_bundle_path(),
64
                                  'data/error_page.tmpl')
65
66
HOME_PAGE_GCONF_KEY = '/desktop/sugar/browser/home_page'
67
68
69
class CommandListener(object):
70
    def __init__(self, window):
71
        self._window = window
72
73
    def handleEvent(self, event):
74
        if not event.isTrusted:
75
            return
76
77
        uri = event.originalTarget.ownerDocument.documentURI
78
        if not uri.startswith('about:neterror?e=nssBadCert'):
79
            return
80
81
        cls = components.classes['@sugarlabs.org/add-cert-exception;1']
82
        cert_exception = cls.createInstance(interfaces.hulahopAddCertException)
83
        cert_exception.showDialog(self._window)
84
85
86
class TabbedView(BrowserNotebook):
87
    __gtype_name__ = 'TabbedView'
88
89
    __gsignals__ = {
90
        'focus-url-entry': (GObject.SignalFlags.RUN_FIRST,
91
                            None,
92
                            ([])),
93
    }
94
95
    def __init__(self):
96
        BrowserNotebook.__init__(self)
97
98
        self.props.show_border = False
99
        self.props.scrollable = True
100
101
        # Used to connect and disconnect functions when 'switch-page'
102
        self._browser = None
103
        self._load_status_changed_hid = None
104
105
        self.connect('size-allocate', self.__size_allocate_cb)
106
        self.connect('page-added', self.__page_added_cb)
107
        self.connect('page-removed', self.__page_removed_cb)
108
109
        self.connect_after('switch-page', self.__switch_page_cb)
110
111
        self.add_tab()
112
        self._update_closing_buttons()
113
        self._update_tab_sizes()
114
115
    def __switch_page_cb(self, tabbed_view, page, page_num):
116
        if tabbed_view.get_n_pages():
117
            self._connect_to_browser(tabbed_view.props.current_browser)
118
119
    def _connect_to_browser(self, browser):
120
        if self._browser is not None:
121
            self._browser.disconnect(self._load_status_changed_hid)
122
123
        self._browser = browser
124
        self._load_status_changed_hid = self._browser.connect(
125
            'notify::load-status', self.__load_status_changed_cb)
126
127
    def normalize_or_autosearch_url(self, url):
128
        """Normalize the url input or return a url for search.
129
130
        We use SoupURI as an indication of whether the value given in url
131
        is not something we want to search; we only do that, though, if
132
        the address has a web scheme, because SoupURI will consider any
133
        string: as a valid scheme, and we will end up prepending http://
134
        to it.
135
136
        This code is borrowed from Epiphany.
137
138
        url -- input string that can be normalized to an url or serve
139
               as search
140
141
        Return: a string containing a valid url
142
143
        """
144
        def has_web_scheme(address):
145
            if address == '':
146
                return False
147
148
            scheme, sep, after = address.partition(':')
149
            if sep == '':
150
                return False
151
152
            return scheme in _WEB_SCHEMES
153
154
        soup_uri = None
155
        effective_url = None
156
157
        if has_web_scheme(url):
158
            try:
159
                soup_uri = Soup.URI.new(url)
160
            except TypeError:
161
                pass
162
163
        if soup_uri is None and not _NON_SEARCH_REGEX.match(url):
164
            # Get the user's LANG to use as default language of
165
            # the results
166
            locale = os.environ.get('LANG', '')
167
            language_location = locale.split('.', 1)[0].lower()
168
            language = language_location.split('_')[0]
169
            # If the string doesn't look like an URI, let's search it:
170
            url_search = 'http://www.google.com/search?' \
171
                'q=%(query)s&ie=UTF-8&oe=UTF-8&hl=%(language)s'
172
            query_param = Soup.form_encode_hash({'q': url})
173
            # [2:] here is getting rid of 'q=':
174
            effective_url = url_search % {'query': query_param[2:],
175
                                          'language': language}
176
        else:
177
            if has_web_scheme(url):
178
                effective_url = url
179
            else:
180
                effective_url = 'http://' + url
181
182
        return effective_url
183
184
    def __size_allocate_cb(self, widget, allocation):
185
        self._update_tab_sizes()
186
187
    def __page_added_cb(self, notebook, child, pagenum):
188
        self._update_closing_buttons()
189
        self._update_tab_sizes()
190
191
    def __page_removed_cb(self, notebook, child, pagenum):
192
        if self.get_n_pages():
193
            self._update_closing_buttons()
194
            self._update_tab_sizes()
195
196
    def __new_tab_cb(self, browser, url):
197
        new_browser = self.add_tab(next_to_current=True)
198
        new_browser.load_uri(url)
199
        new_browser.grab_focus()
200
201
    def __create_web_view_cb(self, web_view, frame):
202
        new_web_view = Browser()
203
        new_web_view.connect('web-view-ready', self.__web_view_ready_cb)
204
        return new_web_view
205
206
    def __web_view_ready_cb(self, web_view):
207
        """
208
        Handle new window requested and open it in a new tab.
209
210
        This callback is called when the WebKit.WebView request for a
211
        new window to open (for example a call to the Javascript
212
        function 'window.open()' or target="_blank")
213
214
        web_view -- the new browser there the url of the
215
                    window.open() call will be loaded.
216
217
                    This object is created in the signal callback
218
                    'create-web-view'.
219
        """
220
221
        web_view.connect('new-tab', self.__new_tab_cb)
222
        web_view.connect('open-pdf', self.__open_pdf_in_new_tab_cb)
223
        web_view.connect('create-web-view', self.__create_web_view_cb)
224
        web_view.grab_focus()
225
226
        self._insert_tab_next(web_view)
227
228
    def __open_pdf_in_new_tab_cb(self, browser, url):
229
        tab_page = PDFTabPage()
230
        tab_page.browser.connect('new-tab', self.__new_tab_cb)
231
        tab_page.browser.connect('tab-close', self.__tab_close_cb)
232
233
        label = TabLabel(tab_page.browser)
234
        label.connect('tab-close', self.__tab_close_cb, tab_page)
235
236
        next_index = self.get_current_page() + 1
237
        self.insert_page(tab_page, label, next_index)
238
        tab_page.show()
239
        label.show()
240
        self.set_current_page(next_index)
241
        tab_page.setup(url)
242
243
    def __load_status_changed_cb(self, widget, param):
244
        if self.get_window() is None:
245
            return
246
247
        status = widget.get_load_status()
248
        if status in (WebKit.LoadStatus.PROVISIONAL,
249
                      WebKit.LoadStatus.COMMITTED,
250
                      WebKit.LoadStatus.FIRST_VISUALLY_NON_EMPTY_LAYOUT):
251
            self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH))
252
        elif status in (WebKit.LoadStatus.FAILED,
253
                        WebKit.LoadStatus.FINISHED):
254
            self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))
255
256
    def add_tab(self, next_to_current=False):
257
        browser = Browser()
258
        browser.connect('new-tab', self.__new_tab_cb)
259
        browser.connect('open-pdf', self.__open_pdf_in_new_tab_cb)
260
        browser.connect('web-view-ready', self.__web_view_ready_cb)
261
        browser.connect('create-web-view', self.__create_web_view_cb)
262
263
        if next_to_current:
264
            self._insert_tab_next(browser)
265
        else:
266
            self._append_tab(browser)
267
        self.emit('focus-url-entry')
268
        return browser
269
270
    def _insert_tab_next(self, browser):
271
        tab_page = TabPage(browser)
272
        label = TabLabel(browser)
273
        label.connect('tab-close', self.__tab_close_cb, tab_page)
274
275
        next_index = self.get_current_page() + 1
276
        self.insert_page(tab_page, label, next_index)
277
        tab_page.show()
278
        self.set_current_page(next_index)
279
280
    def _append_tab(self, browser):
281
        tab_page = TabPage(browser)
282
        label = TabLabel(browser)
283
        label.connect('tab-close', self.__tab_close_cb, tab_page)
284
285
        self.append_page(tab_page, label)
286
        tab_page.show()
287
        self.set_current_page(-1)
288
289
    def on_add_tab(self, gobject):
290
        self.add_tab()
291
292
    def close_tab(self, tab_page=None):
293
        if self.get_n_pages() == 1:
294
            return
295
296
        if tab_page is None:
297
            tab_page = self.get_nth_page(self.get_current_page())
298
299
        if isinstance(tab_page, PDFTabPage):
300
            if tab_page.props.browser.props.load_status < \
301
                    WebKit.LoadStatus.FINISHED:
302
                tab_page.cancel_download()
303
304
        self.remove_page(self.page_num(tab_page))
305
306
        current_page = self.get_nth_page(self.get_current_page())
307
        current_page.props.browser.grab_focus()
308
309
    def __tab_close_cb(self, label, tab_page):
310
        self.close_tab(tab_page)
311
312
    def _update_tab_sizes(self):
313
        """Update tab widths based in the amount of tabs."""
314
315
        n_pages = self.get_n_pages()
316
        canvas_size = self.get_allocation()
317
        allowed_size = canvas_size.width
318
        if n_pages == 1:
319
            # use half of the whole space
320
            tab_expand = False
321
            tab_new_size = int(allowed_size / 2)
322
        elif n_pages <= 8:  # ensure eight tabs
323
            tab_expand = True  # use all the space available by tabs
324
            tab_new_size = -1
325
        else:
326
            # scroll the tab toolbar if there are more than 8 tabs
327
            tab_expand = False
328
            tab_new_size = (allowed_size / 8)
329
330
        for page_idx in range(n_pages):
331
            page = self.get_nth_page(page_idx)
332
            label = self.get_tab_label(page)
333
            self.child_set_property(page, 'tab-expand', tab_expand)
334
            label.update_size(tab_new_size)
335
336
    def _update_closing_buttons(self):
337
        """Prevent closing the last tab."""
338
        first_page = self.get_nth_page(0)
339
        first_label = self.get_tab_label(first_page)
340
        if self.get_n_pages() == 1:
341
            first_label.hide_close_button()
342
        else:
343
            first_label.show_close_button()
344
345
    def load_homepage(self, ignore_gconf=False):
346
        browser = self.current_browser
347
        uri_homepage = None
348
        if not ignore_gconf:
349
            client = GConf.Client.get_default()
350
            uri_homepage = client.get_string(HOME_PAGE_GCONF_KEY)
351
        if uri_homepage is not None:
352
            browser.load_uri(uri_homepage)
353
        elif os.path.isfile(LIBRARY_PATH):
354
            browser.load_uri('file://' + LIBRARY_PATH)
355
        else:
356
            default_page = os.path.join(activity.get_bundle_path(),
357
                                        "data/index.html")
358
            browser.load_uri('file://' + default_page)
359
        browser.grab_focus()
360
361
    def set_homepage(self):
362
        uri = self.current_browser.get_uri()
363
        client = GConf.Client.get_default()
364
        client.set_string(HOME_PAGE_GCONF_KEY, uri)
365
366
    def reset_homepage(self):
367
        client = GConf.Client.get_default()
368
        client.unset(HOME_PAGE_GCONF_KEY)
369
370
    def _get_current_browser(self):
371
        if self.get_n_pages():
372
            return self.get_nth_page(self.get_current_page()).browser
373
        else:
374
            return None
375
376
    current_browser = GObject.property(type=object,
377
                                       getter=_get_current_browser)
378
379
    def get_history(self):
380
        tab_histories = []
381
        for index in xrange(0, self.get_n_pages()):
382
            tab_page = self.get_nth_page(index)
383
            tab_histories.append(tab_page.browser.get_history())
384
        return tab_histories
385
386
    def set_history(self, tab_histories):
387
        if tab_histories and isinstance(tab_histories[0], dict):
388
           # Old format, no tabs
389
            tab_histories = [tab_histories]
390
391
        while self.get_n_pages():
392
            self.remove_page(self.get_n_pages() - 1)
393
394
        def is_pdf_history(tab_history):
395
            return (len(tab_history) == 1 and
396
                    tab_history[0]['url'].lower().endswith('pdf'))
397
398
        for tab_history in tab_histories:
399
            if is_pdf_history(tab_history):
400
                url = tab_history[0]['url']
401
                tab_page = PDFTabPage()
402
                tab_page.browser.connect('new-tab', self.__new_tab_cb)
403
                tab_page.browser.connect('tab-close', self.__tab_close_cb)
404
405
                label = TabLabel(tab_page.browser)
406
                label.connect('tab-close', self.__tab_close_cb, tab_page)
407
408
                self.append_page(tab_page, label)
409
                tab_page.show()
410
                label.show()
411
                tab_page.setup(url, title=tab_history[0]['title'])
412
413
            else:
414
                browser = Browser()
415
                browser.connect('new-tab', self.__new_tab_cb)
416
                browser.connect('open-pdf', self.__open_pdf_in_new_tab_cb)
417
                browser.connect('web-view-ready', self.__web_view_ready_cb)
418
                browser.connect('create-web-view', self.__create_web_view_cb)
419
                self._append_tab(browser)
420
                browser.set_history(tab_history)
421
422
    def is_current_page_pdf(self):
423
        index = self.get_current_page()
424
        current_page = self.get_nth_page(index)
425
        return isinstance(current_page, PDFTabPage)
426
427
428
Gtk.rc_parse_string('''
429
    style "browse-tab-close" {
430
        xthickness = 0
431
        ythickness = 0
432
    }
433
    widget "*browse-tab-close" style "browse-tab-close"''')
434
435
436
class TabPage(Gtk.ScrolledWindow):
437
    __gtype_name__ = 'BrowseTabPage'
438
439
    def __init__(self, browser):
440
        GObject.GObject.__init__(self)
441
442
        self._browser = browser
443
444
        self.add(browser)
445
        browser.show()
446
447
    def _get_browser(self):
448
        return self._browser
449
450
    browser = GObject.property(type=object,
451
                               getter=_get_browser)
452
453
454
class TabLabel(Gtk.HBox):
455
    __gtype_name__ = 'BrowseTabLabel'
456
457
    __gsignals__ = {
458
        'tab-close': (GObject.SignalFlags.RUN_FIRST,
459
                      None,
460
                      ([])),
461
    }
462
463
    def __init__(self, browser):
464
        GObject.GObject.__init__(self)
465
466
        browser.connect('notify::title', self.__title_changed_cb)
467
        browser.connect('notify::load-status', self.__load_status_changed_cb)
468
469
        self._title = _('Untitled')
470
        self._label = Gtk.Label(label=self._title)
471
        self._label.set_ellipsize(Pango.EllipsizeMode.END)
472
        self._label.set_alignment(0, 0.5)
473
        self.pack_start(self._label, True, True, 0)
474
        self._label.show()
475
476
        close_tab_icon = Icon(icon_name='browse-close-tab')
477
        button = Gtk.Button()
478
        button.props.relief = Gtk.ReliefStyle.NONE
479
        button.props.focus_on_click = False
480
        icon_box = Gtk.HBox()
481
        icon_box.pack_start(close_tab_icon, True, False, 0)
482
        button.add(icon_box)
483
        button.connect('clicked', self.__button_clicked_cb)
484
        button.set_name('browse-tab-close')
485
        self.pack_start(button, False, True, 0)
486
        close_tab_icon.show()
487
        icon_box.show()
488
        button.show()
489
        self._close_button = button
490
491
    def update_size(self, size):
492
        self.set_size_request(size, -1)
493
494
    def hide_close_button(self):
495
        self._close_button.hide()
496
497
    def show_close_button(self):
498
        self._close_button.show()
499
500
    def __button_clicked_cb(self, button):
501
        self.emit('tab-close')
502
503
    def __title_changed_cb(self, widget, param):
504
        title = widget.props.title
505
        if not title:
506
            title = os.path.basename(widget.props.uri)
507
508
        self._label.set_text(title)
509
        self._title = title
510
511
    def __load_status_changed_cb(self, widget, param):
512
        status = widget.get_load_status()
513
514
        if status == WebKit.LoadStatus.FAILED:
515
            self._label.set_text(self._title)
516
        elif WebKit.LoadStatus.PROVISIONAL <= status \
517
                < WebKit.LoadStatus.FINISHED:
518
            self._label.set_text(_('Loading...'))
519
        elif status == WebKit.LoadStatus.FINISHED:
520
            if widget.props.title == None:
521
                self._label.set_text(_('Untitled'))
522
                self._title = _('Untitled')
523
524
525
class Browser(WebKit.WebView):
526
    __gtype_name__ = 'Browser'
527
528
    __gsignals__ = {
529
        'new-tab': (GObject.SignalFlags.RUN_FIRST,
530
                    None,
531
                    ([str])),
532
        'open-pdf': (GObject.SignalFlags.RUN_FIRST,
533
                     None,
534
                     ([str])),
535
        'security-status-changed': (GObject.SignalFlags.RUN_FIRST,
536
                                    None,
537
                                    ([])),
538
    }
539
540
    CURRENT_SUGAR_VERSION = '0.100'
541
542
    SECURITY_STATUS_SECURE = 1
543
    SECURITY_STATUS_INSECURE = 2
544
545
    def __init__(self):
546
        WebKit.WebView.__init__(self)
547
548
        web_settings = self.get_settings()
549
550
        # Add SugarLabs user agent:
551
        identifier = ' SugarLabs/' + self.CURRENT_SUGAR_VERSION
552
        web_settings.props.user_agent += identifier
553
554
        # Change font size based in the GtkSettings font size.  The
555
        # gtk-font-name property is a string with format '[font name]
556
        # [font size]' like 'Sans Serif 10'.
557
        gtk_settings = Gtk.Settings.get_default()
558
        gtk_font_name = gtk_settings.get_property('gtk-font-name')
559
        gtk_font_size = float(gtk_font_name.split()[-1])
560
        web_settings.props.default_font_size = gtk_font_size * 1.2
561
        web_settings.props.default_monospace_font_size = \
562
            gtk_font_size * 1.2 - 2
563
564
        self.set_settings(web_settings)
565
566
        # Scale text and graphics:
567
        self.set_full_content_zoom(True)
568
569
        # This property is used to set the title immediatly the user
570
        # presses Enter on the URL Entry
571
        self.loading_uri = None
572
573
        self.security_status = None
574
575
        # Reference to the global history and callbacks to handle it:
576
        self._global_history = globalhistory.get_global_history()
577
        self.connect('notify::load-status', self.__load_status_changed_cb)
578
        self.connect('notify::title', self.__title_changed_cb)
579
        self.connect('download-requested', self.__download_requested_cb)
580
        self.connect('mime-type-policy-decision-requested',
581
                     self.__mime_type_policy_cb)
582
        self.connect('load-error', self.__load_error_cb)
583
584
        self._inject_media_style = False
585
586
        ContentInvoker(self)
587
588
        try:
589
            self.connect('run-file-chooser', self.__run_file_chooser)
590
        except TypeError:
591
            # Only present in WebKit1 > 1.9.3 and WebKit2
592
            pass
593
594
    def get_history(self):
595
        """Return the browsing history of this browser."""
596
        back_forward_list = self.get_back_forward_list()
597
        items_list = self._items_history_as_list(back_forward_list)
598
599
        # If this is an empty tab, return an empty history:
600
        if len(items_list) == 1 and items_list[0] is None:
601
            return []
602
603
        history = []
604
        for item in items_list:
605
            history.append({'url': item.get_uri(),
606
                            'title': item.get_title()})
607
608
        return history
609
610
    def set_history(self, history):
611
        """Restore the browsing history for this browser."""
612
        back_forward_list = self.get_back_forward_list()
613
        back_forward_list.clear()
614
        for entry in history:
615
            uri, title = entry['url'], entry['title']
616
            history_item = WebKit.WebHistoryItem.new_with_data(uri, title)
617
            back_forward_list.add_item(history_item)
618
619
    def get_history_index(self):
620
        """Return the index of the current item in the history."""
621
        back_forward_list = self.get_back_forward_list()
622
        history_list = self._items_history_as_list(back_forward_list)
623
        current_item = back_forward_list.get_current_item()
624
        return history_list.index(current_item)
625
626
    def set_history_index(self, index):
627
        """Go to the item in the history specified by the index."""
628
        back_forward_list = self.get_back_forward_list()
629
        current_item = index - back_forward_list.get_back_length()
630
        item = back_forward_list.get_nth_item(current_item)
631
        if item is not None:
632
            self.go_to_back_forward_item(item)
633
634
    def _items_history_as_list(self, history):
635
        """Return a list with the items of a WebKit.WebBackForwardList."""
636
        back_items = []
637
        for n in reversed(range(1, history.get_back_length() + 1)):
638
            item = history.get_nth_item(n * -1)
639
            back_items.append(item)
640
641
        current_item = [history.get_current_item()]
642
643
        forward_items = []
644
        for n in range(1, history.get_forward_length() + 1):
645
            item = history.get_nth_item(n)
646
            forward_items.append(item)
647
648
        all_items = back_items + current_item + forward_items
649
        return all_items
650
651
    def get_source(self, async_cb, async_err_cb):
652
        data_source = self.get_main_frame().get_data_source()
653
        data = data_source.get_data()
654
        if data_source.is_loading() or data is None:
655
            async_err_cb()
656
        temp_path = os.path.join(activity.get_activity_root(), 'instance')
657
        file_path = os.path.join(temp_path, '%i' % time.time())
658
659
        file_handle = file(file_path, 'w')
660
        file_handle.write(data.str)
661
        file_handle.close()
662
        async_cb(file_path)
663
664
    def open_new_tab(self, url):
665
        self.emit('new-tab', url)
666
667
    def __run_file_chooser(self, browser, request):
668
        picker = FilePicker(self)
669
        chosen = picker.run()
670
        picker.destroy()
671
672
        if chosen:
673
            request.select_files([chosen])
674
        elif hasattr(request, 'cancel'):
675
            # WebKit2 only
676
            request.cancel()
677
        return True
678
679
    def __load_status_changed_cb(self, widget, param):
680
        status = widget.get_load_status()
681
        if status <= WebKit.LoadStatus.COMMITTED:
682
            # Add the url to the global history or update it.
683
            uri = self.get_uri()
684
            self._global_history.add_page(uri)
685
686
        if status == WebKit.LoadStatus.COMMITTED:
687
            # Update the security status.
688
            response = widget.get_main_frame().get_network_response()
689
            message = response.get_message()
690
            if message:
691
                use_https, certificate, tls_errors = message.get_https_status()
692
693
                if use_https:
694
                    if tls_errors == 0:
695
                        self.security_status = self.SECURITY_STATUS_SECURE
696
                    else:
697
                        self.security_status = self.SECURITY_STATUS_INSECURE
698
                else:
699
                    self.security_status = None
700
                self.emit('security-status-changed')
701
702
    def __title_changed_cb(self, widget, param):
703
        """Update title in global history."""
704
        uri = self.get_uri()
705
        if self.props.title is not None:
706
            title = self.props.title
707
            if not isinstance(title, unicode):
708
                title = unicode(title, 'utf-8')
709
            self._global_history.set_page_title(uri, title)
710
711
    def __mime_type_policy_cb(self, webview, frame, request, mimetype,
712
                              policy_decision):
713
        """Handle downloads and PDF files."""
714
        if mimetype == 'application/pdf':
715
            self.emit('open-pdf', request.get_uri())
716
            policy_decision.ignore()
717
            return True
718
719
        elif mimetype == 'audio/x-vorbis+ogg':
720
            self._inject_media_style = True
721
722
        elif not self.can_show_mime_type(mimetype):
723
            policy_decision.download()
724
            return True
725
726
        return False
727
728
    def __download_requested_cb(self, browser, download):
729
        downloadmanager.add_download(download, browser)
730
        return True
731
732
    def __load_error_cb(self, web_view, web_frame, uri, web_error):
733
        """Show Sugar's error page"""
734
735
        # Don't show error page if the load was interrupted by policy
736
        # change or the request is going to be handled by a
737
        # plugin. For example, if a file was requested for download or
738
        # an .ogg file is going to be played.
739
        if web_error.code in (WebKit.PolicyError.\
740
                FRAME_LOAD_INTERRUPTED_BY_POLICY_CHANGE,
741
                WebKit.PluginError.WILL_HANDLE_LOAD):
742
            if self._inject_media_style:
743
                css_style_file = open(os.path.join(activity.get_bundle_path(),
744
                                                   "data/media-controls.css"))
745
                css_style = css_style_file.read().replace('\n', '')
746
                inject_style_script = \
747
                    "var style = document.createElement('style');" \
748
                    "style.innerHTML = '%s';" \
749
                    "document.body.appendChild(style);" % css_style
750
                web_view.execute_script(inject_style_script)
751
            return True
752
753
        data = {
754
            'page_title': _('This web page could not be loaded'),
755
            'title': _('This web page could not be loaded'),
756
            'message': _('"%s" could not be loaded. Please check for '
757
                         'typing errors, and make sure you are connected '
758
                         'to the Internet.') % uri,
759
            'btn_value': _('Try again'),
760
            'url': uri,
761
            }
762
763
        html = open(DEFAULT_ERROR_PAGE, 'r').read() % data
764
        web_frame.load_alternate_string(html, uri, uri)
765
766
        return True
767
768
769
class PopupDialog(Gtk.Window):
770
    def __init__(self):
771
        GObject.GObject.__init__(self)
772
773
        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
774
775
        border = style.GRID_CELL_SIZE
776
        self.set_default_size(Gdk.Screen.width() - border * 2,
777
                              Gdk.Screen.height() - border * 2)
778
779
        self.view = WebKit.WebView()
780
        self.view.connect('notify::visibility', self.__notify_visibility_cb)
781
        self.add(self.view)
782
        self.view.realize()
783
784
    def __notify_visibility_cb(self, web_view, pspec):
785
        if self.view.props.visibility:
786
            self.view.show()
787
            self.show()