From d12bd7718e913a0d57e47a7bb0e78cbe62e34513 Mon Sep 17 00:00:00 2001 From: Pootle daemon Date: Fri, 06 Jan 2012 05:32:49 +0000 Subject: Merge branch 'master' of git.sugarlabs.org:browse/mainline --- diff --git a/browser.py b/browser.py index f6efe26..0ff0b77 100644 --- a/browser.py +++ b/browser.py @@ -18,6 +18,7 @@ import os import time +import re from gettext import gettext as _ from gi.repository import GObject @@ -25,51 +26,32 @@ from gi.repository import Gtk from gi.repository import Gdk from gi.repository import Pango from gi.repository import WebKit +from gi.repository import Soup from sugar3 import env from sugar3.activity import activity from sugar3.graphics import style from sugar3.graphics.icon import Icon -# FIXME -# from palettes import ContentInvoker -# from sessionhistory import HistoryListener -# from progresslistener import ProgressListener from widgets import BrowserNotebook _ZOOM_AMOUNT = 0.1 _LIBRARY_PATH = '/usr/share/library-common/index.html' +_WEB_SCHEMES = ['http', 'https', 'ftp', 'file', 'javascript', 'data', + 'about', 'gopher', 'mailto'] -class SaveListener(object): - def __init__(self, user_data, callback): - self._user_data = user_data - self._callback = callback - - def onStateChange(self, webProgress, request, stateFlags, status): - listener_class = interfaces.nsIWebProgressListener - if (stateFlags & listener_class.STATE_IS_REQUEST and - stateFlags & listener_class.STATE_STOP): - self._callback(self._user_data) - - # Contrary to the documentation, STATE_IS_REQUEST is _not_ always set - # if STATE_IS_DOCUMENT is set. - if (stateFlags & listener_class.STATE_IS_DOCUMENT and - stateFlags & listener_class.STATE_STOP): - self._callback(self._user_data) - - def onProgressChange(self, progress, request, curSelfProgress, - maxSelfProgress, curTotalProgress, maxTotalProgress): - pass - - def onLocationChange(self, progress, request, location): - pass - - def onStatusChange(self, progress, request, status, message): - pass - - def onSecurityChange(self, progress, request, state): - pass +_NON_SEARCH_REGEX = re.compile(''' + (^localhost(\\.[^\s]+)?(:\\d+)?(/.*)?$| + ^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]$| + ^::[0-9a-f:]*$| # IPv6 literals + ^[0-9a-f:]+:[0-9a-f:]*$| # IPv6 literals + ^[^\\.\s]+\\.[^\\.\s]+.*$| # foo.bar... + ^https?://[^/\\.\s]+.*$| + ^about:.*$| + ^data:.*$| + ^file:.*$) + ''', re.VERBOSE) class CommandListener(object): @@ -98,49 +80,12 @@ class TabbedView(BrowserNotebook): ([])), } - AGENT_SHEET = os.path.join(activity.get_bundle_path(), - 'agent-stylesheet.css') - USER_SHEET = os.path.join(env.get_profile_path(), 'gecko', - 'user-stylesheet.css') - def __init__(self): BrowserNotebook.__init__(self) self.props.show_border = False self.props.scrollable = True - # FIXME - # io_service_class = components.classes[ \ - # "@mozilla.org/network/io-service;1"] - # io_service = io_service_class.getService(interfaces.nsIIOService) - - # # Use xpcom to turn off "offline mode" detection, which disables - # # access to localhost for no good reason. (Trac #6250.) - # io_service2 = io_service_class.getService(interfaces.nsIIOService2) - # io_service2.manageOfflineStatus = False - - # cls = components.classes['@mozilla.org/content/style-sheet-service;1'] - # style_sheet_service = cls.getService(interfaces.nsIStyleSheetService) - - # if os.path.exists(TabbedView.AGENT_SHEET): - # agent_sheet_uri = io_service.newURI('file:///' + - # TabbedView.AGENT_SHEET, - # None, None) - # style_sheet_service.loadAndRegisterSheet(agent_sheet_uri, - # interfaces.nsIStyleSheetService.AGENT_SHEET) - - # if os.path.exists(TabbedView.USER_SHEET): - # url = 'file:///' + TabbedView.USER_SHEET - # user_sheet_uri = io_service.newURI(url, None, None) - # style_sheet_service.loadAndRegisterSheet(user_sheet_uri, - # interfaces.nsIStyleSheetService.USER_SHEET) - - # cls = components.classes['@mozilla.org/embedcomp/window-watcher;1'] - # window_watcher = cls.getService(interfaces.nsIWindowWatcher) - # window_creator = xpcom.server.WrapObject(self, - # interfaces.nsIWindowCreator) - # window_watcher.setWindowCreator(window_creator) - self.connect('size-allocate', self.__size_allocate_cb) self.connect('page-added', self.__page_added_cb) self.connect('page-removed', self.__page_removed_cb) @@ -149,28 +94,56 @@ class TabbedView(BrowserNotebook): self._update_closing_buttons() self._update_tab_sizes() - def createChromeWindow(self, parent, flags): - if flags & interfaces.nsIWebBrowserChrome.CHROME_OPENAS_CHROME: - dialog = PopupDialog() - dialog.view.is_chrome = True + def normalize_or_autosearch_url(self, url): + """Normalize the url input or return a url for search. - parent_dom_window = parent.webBrowser.contentDOMWindow - parent_view = hulahop.get_view_for_window(parent_dom_window) - if parent_view: - dialog.set_transient_for(parent_view.get_toplevel()) + We use SoupURI as an indication of whether the value given in url + is not something we want to search; we only do that, though, if + the address has a web scheme, because SoupURI will consider any + string: as a valid scheme, and we will end up prepending http:// + to it. - browser = dialog.view.browser + This code is borrowed from Epiphany. - item = browser.queryInterface(interfaces.nsIDocShellTreeItem) - item.itemType = interfaces.nsIDocShellTreeItem.typeChromeWrapper + url -- input string that can be normalized to an url or serve + as search - return browser.containerWindow + Return: a string containing a valid url + + """ + def has_web_scheme(address): + if address == '': + return False + + scheme, sep, after = address.partition(':') + if sep == '': + return False + + return scheme in _WEB_SCHEMES + + soup_uri = None + effective_url = None + + if has_web_scheme(url): + try: + soup_uri = Soup.URI.new(url) + except TypeError: + pass + + if soup_uri is None and not _NON_SEARCH_REGEX.match(url): + # If the string doesn't look like an URI, let's search it: + url_search = \ + _('http://www.google.com/search?q=%s&ie=UTF-8&oe=UTF-8') + query_param = Soup.form_encode_hash({'q': url}) + # [2:] here is getting rid of 'q=': + effective_url = url_search % query_param[2:] else: - browser = Browser() - browser.connect('new-tab', self.__new_tab_cb) - self._append_tab(browser) + if has_web_scheme(url): + effective_url = url + else: + effective_url = 'http://' + url - return browser.browser.containerWindow + return effective_url def __size_allocate_cb(self, widget, allocation): self._update_tab_sizes() @@ -376,9 +349,6 @@ class Browser(WebKit.WebView): __gtype_name__ = 'Browser' __gsignals__ = { - 'is-setup': (GObject.SignalFlags.RUN_FIRST, - None, - ([])), 'new-tab': (GObject.SignalFlags.RUN_FIRST, None, ([str])), @@ -387,38 +357,6 @@ class Browser(WebKit.WebView): def __init__(self): WebKit.WebView.__init__(self) - # FIXME - # self.history = HistoryListener() - # self.progress = ProgressListener() - - def do_setup(self): - WebKit.WebView.do_setup(self) - listener = xpcom.server.WrapObject(ContentInvoker(self), - interfaces.nsIDOMEventListener) - self.window_root.addEventListener('click', listener, False) - - listener = xpcom.server.WrapObject(CommandListener(self.dom_window), - interfaces.nsIDOMEventListener) - self.window_root.addEventListener('command', listener, False) - - self.progress.setup(self) - - self.history.setup(self.web_navigation) - - self.typeahead.init(self.doc_shell) - - self.emit('is-setup') - - def get_url_from_nsiuri(self, uri): - """ - get a nsIURI object and return a string with the url - """ - if uri == None: - return '' - cls = components.classes['@mozilla.org/intl/texttosuburi;1'] - texttosuburi = cls.getService(interfaces.nsITextToSubURI) - return texttosuburi.unEscapeURIForUI(uri.originCharset, uri.spec) - def get_history(self): """Return the browsing history of this browser.""" back_forward_list = self.get_back_forward_list() @@ -476,25 +414,17 @@ class Browser(WebKit.WebView): return all_items def get_source(self, async_cb, async_err_cb): - cls = components.classes[ \ - '@mozilla.org/embedding/browser/nsWebBrowserPersist;1'] - persist = cls.createInstance(interfaces.nsIWebBrowserPersist) - # get the source from the cache - persist.persistFlags = \ - interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE - + data_source = self.get_main_frame().get_data_source() + data = data_source.get_data() + if data_source.is_loading() or data is None: + async_err_cb() temp_path = os.path.join(activity.get_activity_root(), 'instance') file_path = os.path.join(temp_path, '%i' % time.time()) - cls = components.classes["@mozilla.org/file/local;1"] - local_file = cls.createInstance(interfaces.nsILocalFile) - local_file.initWithPath(file_path) - - progresslistener = SaveListener(file_path, async_cb) - persist.progressListener = xpcom.server.WrapObject( - progresslistener, interfaces.nsIWebProgressListener) - uri = self.web_navigation.currentURI - persist.saveURI(uri, self.doc_shell, None, None, None, local_file) + file_handle = file(file_path, 'w') + file_handle.write(data.str) + file_handle.close() + async_cb(file_path) def open_new_tab(self, url): self.emit('new-tab', url) diff --git a/linkbutton.py b/linkbutton.py index 151f74c..a6248bf 100644 --- a/linkbutton.py +++ b/linkbutton.py @@ -20,6 +20,9 @@ from gi.repository import GdkPixbuf import os from gi.repository import GObject +from gi.repository import Gdk +import StringIO +import cairo from gettext import gettext as _ import rsvg import re @@ -47,32 +50,27 @@ class LinkButton(TrayButton, GObject.GObject): def set_image(self, buf, fill='#0000ff', stroke='#4d4c4f'): img = Gtk.Image() - loader = GdkPixbuf.PixbufLoader() - loader.write(buf) - loader.close() - pixbuf = loader.get_pixbuf() - del loader + str_buf = StringIO.StringIO(buf) + thumb_surface = cairo.ImageSurface.create_from_png(str_buf) xo_buddy = os.path.join(os.path.dirname(__file__), "icons/link.svg") - pixbuf_bg = self._read_link_background(xo_buddy, fill, stroke) - pixbuf_bg = pixbuf_bg.scale_simple(style.zoom(120), - style.zoom(110), - GdkPixbuf.InterpType.BILINEAR) + + bg_surface = self._read_link_background(xo_buddy, fill, stroke) + + cairo_context = cairo.Context(bg_surface) dest_x = style.zoom(10) dest_y = style.zoom(20) - w = pixbuf.get_width() - h = pixbuf.get_height() - scale_x = 1 - scale_y = 1 - - pixbuf.composite(pixbuf_bg, dest_x, dest_y, w, h, dest_x, dest_y, - scale_x, scale_y, GdkPixbuf.InterpType.BILINEAR, 255) + cairo_context.set_source_surface(thumb_surface, dest_x, dest_y) + thumb_width, thumb_height = style.zoom(100), style.zoom(80) + cairo_context.rectangle(dest_x, dest_y, thumb_width, thumb_height) + cairo_context.fill() + + bg_width, bg_height = style.zoom(120), style.zoom(110) + pixbuf_bg = Gdk.pixbuf_get_from_surface(bg_surface, 0, 0, + bg_width, bg_height) img.set_from_pixbuf(pixbuf_bg) self.set_icon_widget(img) img.show() - del pixbuf - del pixbuf_bg - gc.collect() def _read_link_background(self, filename, fill_color, stroke_color): icon_file = open(filename, 'r') @@ -87,8 +85,16 @@ class LinkButton(TrayButton, GObject.GObject): entity = '' % stroke_color data = re.sub('', entity, data) - data_size = len(data) - return rsvg.Handle(data=data).get_pixbuf() + link_width, link_height = style.zoom(120), style.zoom(110) + link_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, + link_width, link_height) + link_context = cairo.Context(link_surface) + link_scale_w = link_width * 1.0 / 120 + link_scale_h = link_height * 1.0 / 110 + link_context.scale(link_scale_w, link_scale_h) + handler = rsvg.Handle(data=data) + handler.render_cairo(link_context) + return link_surface def setup_rollover_options(self, info): palette = Palette(info, text_maxlen=50) diff --git a/webactivity.py b/webactivity.py index 4730588..3d741d9 100644 --- a/webactivity.py +++ b/webactivity.py @@ -35,6 +35,7 @@ import cjson from gi.repository import GConf import locale import cairo +import StringIO from hashlib import sha1 from sugar3.activity import activity @@ -504,7 +505,7 @@ class WebActivity(activity.Activity): ''' take screenshot and add link info to the model ''' browser = self._tabbed_view.props.current_browser - ui_uri = browser.get_url_from_nsiuri(browser.progress.location) + ui_uri = browser.get_uri() for link in self.model.data['shared_links']: if link['hash'] == sha1(ui_uri).hexdigest(): @@ -553,31 +554,26 @@ class WebActivity(activity.Activity): ''' an item of the link tray has been clicked ''' self._tabbed_view.props.current_browser.load_uri(url) - def _pixbuf_save_cb(self, buf, data): - data[0] += buf - return True - - def get_buffer(self, pixbuf): - data = [""] - pixbuf.save_to_callback(self._pixbuf_save_cb, "png", {}, data) - return str(data[0]) - def _get_screenshot(self): - window = self._tabbed_view.props.current_browser.window - width, height = window.get_size() + browser = self._tabbed_view.props.current_browser + window = browser.get_window() + width, height = window.get_width(), window.get_height() + + thumb_width, thumb_height = style.zoom(100), style.zoom(80) - screenshot = GdkPixbuf.Pixbuf(GdkPixbuf.Colorspace.RGB, has_alpha=False, - bits_per_sample=8, width=width, - height=height) - screenshot.get_from_drawable(window, window.get_colormap(), 0, 0, 0, 0, - width, height) + thumb_surface = Gdk.Window.create_similar_surface(window, + cairo.CONTENT_COLOR, thumb_width, thumb_height) - screenshot = screenshot.scale_simple(style.zoom(100), - style.zoom(80), - GdkPixbuf.InterpType.BILINEAR) + cairo_context = cairo.Context(thumb_surface) + thumb_scale_w = thumb_width * 1.0 / width + thumb_scale_h = thumb_height * 1.0 / height + cairo_context.scale(thumb_scale_w, thumb_scale_h) + Gdk.cairo_set_source_window(cairo_context, window, 0, 0) + cairo_context.paint() - buf = self.get_buffer(screenshot) - return buf + thumb_str = StringIO.StringIO() + thumb_surface.write_to_png(thumb_str) + return thumb_str.getvalue() def can_close(self): if self._force_close: diff --git a/webtoolbar.py b/webtoolbar.py index 47ece72..c0e1396 100644 --- a/webtoolbar.py +++ b/webtoolbar.py @@ -384,7 +384,9 @@ class PrimaryToolbar(ToolbarBase): def _entry_activate_cb(self, entry): browser = self._tabbed_view.props.current_browser - browser.load_uri(entry.props.text) + url = entry.props.text + effective_url = self._tabbed_view.normalize_or_autosearch_url(url) + browser.load_uri(effective_url) browser.grab_focus() def _go_home_cb(self, button): diff --git a/widgets.py b/widgets.py index 3cfdf1e..d1de3ee 100644 --- a/widgets.py +++ b/widgets.py @@ -59,37 +59,11 @@ class BrowserNotebook(Gtk.Notebook): def __init__(self): GObject.GObject.__init__(self) - self._switch_handler = self.connect('switch-page', - self.__on_switch_page) tab_add = TabAdd() tab_add.connect('tab-added', self.on_add_tab) - empty_page = Gtk.HBox() - self.append_page(empty_page, tab_add) - empty_page.show() + self.set_action_widget(tab_add, Gtk.PackType.END) + tab_add.show() def on_add_tab(self, obj): raise NotImplementedError("implement this in the subclass") - - def __on_switch_page(self, notebook, page, page_num): - """Don't switch to the extra tab at the end.""" - if page_num > 0 and page_num == Gtk.Notebook.get_n_pages(self) - 1: - self.handler_block(self._switch_handler) - self.set_current_page(-1) - self.handler_unblock(self._switch_handler) - self.connect('switch-page', self.__on_switch_page) - self.stop_emission("switch-page") - - def get_n_pages(self): - """Skip the extra tab at the end on the pages count.""" - return Gtk.Notebook.get_n_pages(self) - 1 - - def append_page(self, page, label): - """Append keeping the extra tab at the end.""" - return self.insert_page(page, label, self.get_n_pages()) - - def set_current_page(self, number): - """If indexing from the end, skip the extra tab.""" - if number < 0: - number = self.get_n_pages() - 1 - Gtk.Notebook.set_current_page(self, number) -- cgit v0.9.1