Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--downloadmanager.py90
-rw-r--r--palettes.py19
2 files changed, 105 insertions, 4 deletions
diff --git a/downloadmanager.py b/downloadmanager.py
index 4b6e84f..8d5fae9 100644
--- a/downloadmanager.py
+++ b/downloadmanager.py
@@ -23,6 +23,7 @@ import urlparse
import gtk
import hulahop
+import xpcom
from xpcom.nsError import *
from xpcom import components
from xpcom.components import interfaces
@@ -43,7 +44,8 @@ if dbus.version >= (0, 82, 3):
else:
DBUS_PYTHON_TIMEOUT_UNITS_PER_SECOND = 1000
-NS_BINDING_ABORTED = 0x804b0002 # From nsNetError.h
+NS_BINDING_ABORTED = 0x804b0002 # From nsNetError.h
+NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020 # From nsURILoader.h
DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
@@ -285,3 +287,89 @@ components.registrar.registerFactory('{23c51569-e9a1-4a92-adeb-3723db82ef7c}',
'Sugar Download',
'@mozilla.org/transfer;1',
Factory(Download))
+
+def save_link(url, text, owner_document):
+ # Inspired on Firefox' browser/base/content/nsContextMenu.js:saveLink()
+
+ cls = components.classes["@mozilla.org/network/io-service;1"]
+ io_service = cls.getService(interfaces.nsIIOService)
+ uri = io_service.newURI(url, None, None)
+ channel = io_service.newChannelFromURI(uri)
+
+ auth_prompt_callback = xpcom.server.WrapObject(
+ _AuthPromptCallback(owner_document.defaultView),
+ interfaces.nsIInterfaceRequestor)
+ channel.notificationCallbacks = auth_prompt_callback
+
+ channel.loadFlags = channel.loadFlags | \
+ interfaces.nsIRequest.LOAD_BYPASS_CACHE | \
+ interfaces.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS
+
+ if _implements_interface(channel, interfaces.nsIHttpChannel):
+ channel.referrer = io_service.newURI(owner_document.documentURI, None,
+ None)
+
+ # kick off the channel with our proxy object as the listener
+ listener = xpcom.server.WrapObject(
+ _SaveLinkProgressListener(owner_document),
+ interfaces.nsIStreamListener)
+ channel.asyncOpen(listener, None)
+
+def _implements_interface(obj, interface):
+ try:
+ obj.QueryInterface(interface)
+ return True
+ except xpcom.Exception, e:
+ if e.errno == NS_NOINTERFACE:
+ return False
+ else:
+ raise
+
+class _AuthPromptCallback(object):
+ _com_interfaces_ = interfaces.nsIInterfaceRequestor
+
+ def __init__(self, dom_window):
+ self._dom_window = dom_window
+
+ def getInterface(self, uuid):
+ if uuid in [interfaces.nsIAuthPrompt, interfaces.nsIAuthPrompt2]:
+ cls = components.classes["@mozilla.org/embedcomp/window-watcher;1"]
+ window_watcher = cls.getService(interfaces.nsIPromptFactory)
+ return window_watcher.getPrompt(self._dom_window, uuid)
+ return None
+
+class _SaveLinkProgressListener(object):
+ _com_interfaces_ = interfaces.nsIStreamListener
+
+ """ an object to proxy the data through to
+ nsIExternalHelperAppService.doContent, which will wait for the appropriate
+ MIME-type headers and then prompt the user with a file picker
+ """
+
+ def __init__(self, owner_document):
+ self._owner_document = owner_document
+ self._external_listener = None
+
+ def onStartRequest(self, request, context):
+ if request.status != NS_OK:
+ logging.error("Error downloading link")
+ return
+
+ cls = components.classes[
+ "@mozilla.org/uriloader/external-helper-app-service;1"]
+ external_helper = cls.getService(interfaces.nsIExternalHelperAppService)
+
+ channel = request.QueryInterface(interfaces.nsIChannel)
+
+ self._external_listener = \
+ external_helper.doContent(channel.contentType, request,
+ self._owner_document.defaultView, True)
+ self._external_listener.onStartRequest(request, context)
+
+ def onStopRequest(self, request, context, statusCode):
+ self._external_listener.onStopRequest(request, context, statusCode)
+
+ def onDataAvailable(self, request, context, inputStream, offset, count):
+ self._external_listener.onDataAvailable(request, context, inputStream,
+ offset, count);
+
diff --git a/palettes.py b/palettes.py
index bae47a5..feb6ddb 100644
--- a/palettes.py
+++ b/palettes.py
@@ -30,6 +30,8 @@ from sugar.graphics.icon import Icon
from sugar import profile
from sugar.activity import activity
+import downloadmanager
+
class ContentInvoker(Invoker):
_com_interfaces_ = interfaces.nsIDOMEventListener
@@ -59,7 +61,8 @@ class ContentInvoker(Invoker):
else:
title = None
- self.palette = LinkPalette(self._browser, title, target.href)
+ self.palette = LinkPalette(self._browser, title, target.href,
+ target.ownerDocument)
self.notify_right_click()
elif target.tagName.lower() == 'img':
if target.title:
@@ -75,11 +78,13 @@ class ContentInvoker(Invoker):
self.notify_right_click()
class LinkPalette(Palette):
- def __init__(self, browser, title, url):
+ def __init__(self, browser, title, url, owner_document):
Palette.__init__(self)
- self._url = url
self._browser = browser
+ self._title = title
+ self._url = url
+ self._owner_document = owner_document
if title is not None:
self.props.primary_text = title
@@ -100,6 +105,11 @@ class LinkPalette(Palette):
self.menu.append(menu_item)
menu_item.show()
+ menu_item = MenuItem(_('Download link'))
+ menu_item.connect('activate', self.__download_activate_cb)
+ self.menu.append(menu_item)
+ menu_item.show()
+
def __follow_activate_cb(self, menu_item):
self._browser.load_uri(self._url)
self._browser.grab_focus()
@@ -130,6 +140,9 @@ class LinkPalette(Palette):
def __clipboard_clear_func_cb(self, clipboard, data):
pass
+ def __download_activate_cb(self, menu_item):
+ downloadmanager.save_link(self._url, self._title, self._owner_document)
+
class ImagePalette(Palette):
def __init__(self, title, url):
Palette.__init__(self)