From 2c3b4bee71d588f853139ea33e4fc95aff391350 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 13 Jun 2007 19:14:28 +0000 Subject: Add target text/uri-list for images when dragging them out of mozilla. --- diff --git a/browser.py b/browser.py index 87251d5..4f90ccb 100644 --- a/browser.py +++ b/browser.py @@ -26,6 +26,7 @@ from xpcom.components import interfaces from hulahop.webview import WebView import sessionstore +from dnd import DragDropHooks class Browser(WebView): def __init__(self): @@ -36,7 +37,20 @@ class Browser(WebView): window_watcher = cls.getService(interfaces.nsIWindowWatcher) window_watcher.setWindowCreator(window_creator) - self.is_chrome= False + self.is_chrome = False + + self.connect('realize', self._realize_cb) + + def _realize_cb(self, widget): + drag_drop_hooks = DragDropHooks(self) + + cls = components.classes['@mozilla.org/embedcomp/command-params;1'] + cmd_params = cls.createInstance('nsICommandParams') + cmd_params.setISupportsValue('addhook', drag_drop_hooks) + + requestor = self.browser.queryInterface(interfaces.nsIInterfaceRequestor) + command_manager = requestor.getInterface(interfaces.nsICommandManager) + command_manager.doCommand('cmd_clipboardDragDropHook', cmd_params, self.window) def get_session(self): return sessionstore.get_session(self) diff --git a/dnd.py b/dnd.py new file mode 100644 index 0000000..b916276 --- /dev/null +++ b/dnd.py @@ -0,0 +1,88 @@ +# Copyright (C) 2007, One Laptop Per Child +# +# 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 +# (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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import os.path +import tempfile + +from xpcom.nsError import * +from xpcom import COMException +from xpcom import components +from xpcom.components import interfaces + +from documentnode import DocumentNode + +class UriListDataProvider: + _com_interfaces_ = interfaces.nsIFlavorDataProvider + + def __init__(self, doc_node): + self._doc_node = doc_node + + def getFlavorData(self, transferable, flavor): + if flavor != 'text/uri-list': + raise COMException(NS_ERROR_NOT_IMPLEMENTED) + + mime_type = self._doc_node.get_image_mime_type() + image_name = self._doc_node.get_image_name() + image_name, file_ext = os.path.splitext(image_name) + + if file_ext: + file_ext = file_ext[1:] + + cls = components.classes['@mozilla.org/mime;1'] + mime_service = cls.getService(interfaces.nsIMIMEService) + file_ext = mime_service.getPrimaryExtension(mime_type, file_ext) + + f, file_path = tempfile.mkstemp(image_name + '.' + file_ext) + del f + + self._doc_node.save_image(file_path) + + cls = components.classes['@mozilla.org/supports-string;1'] + string_supports = cls.createInstance(interfaces.nsISupportsString) + string_supports.data = 'file://' + file_path + + return string_supports, 32 + +class DragDropHooks: + _com_interfaces_ = interfaces.nsIClipboardDragDropHooks + + def __init__(self, browser): + self._browser = browser + + def allowDrop(self, event, session): + raise COMException(NS_ERROR_NOT_IMPLEMENTED) + + def allowStartDrag(self, event): + raise COMException(NS_ERROR_NOT_IMPLEMENTED) + + def onPasteOrDrop(self, event, trans): + return False + + def onCopyOrDrag(self, event, trans): + mouse_event = event.queryInterface(interfaces.nsIDOMMouseEvent) + event_target = mouse_event.target + target_node = event_target.queryInterface(interfaces.nsIDOMNode) + document_node = DocumentNode(target_node, self._browser.web_navigation) + + if document_node.is_image(): + trans.removeDataFlavor('text/x-moz-url') + trans.addDataFlavor('text/uri-list') + + data_provider = UriListDataProvider(document_node) + trans.setTransferData('text/uri-list', data_provider, 0) + + return True + diff --git a/documentnode.py b/documentnode.py new file mode 100644 index 0000000..ead07b6 --- /dev/null +++ b/documentnode.py @@ -0,0 +1,146 @@ +# Copyright (C) 2007, One Laptop Per Child +# +# 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 +# (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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import os.path +import urlparse + +from xpcom.nsError import * +from xpcom import COMException +from xpcom import components +from xpcom.components import interfaces + +class DocumentNode: + def __init__(self, DOM_node, web_navigation): + self._DOM_node = DOM_node + self._image_element = None + self._image_name = None + self._image_mime_type = None + self._image_properties = None + self._web_navigation = web_navigation + + def is_image(self): + if self._image_element: + return True + + if self._DOM_node.nodeType == interfaces.nsIDOMNode.ELEMENT_NODE: + element = self._DOM_node.queryInterface(interfaces.nsIDOMHTMLElement) + if element.localName.lower() == "img": + self._image_element = element.queryInterface( + interfaces.nsIDOMHTMLImageElement) + return True + + return False + + def _get_image_properties(self): + if not self.is_image(): + return None + + if not self._image_properties: + cls = components.classes['@mozilla.org/network/simple-uri;1'] + uri = cls.createInstance(interfaces.nsIURI) + uri.spec = self._image_element.src + + cls = components.classes['@mozilla.org/image/cache;1'] + image_cache = cls.getService(interfaces.imgICache) + self._image_properties = image_cache.findEntryProperties(uri) + + return self._image_properties + + def get_image_mime_type(self): + if not self.is_image(): + return None + + if not self._image_mime_type: + properties = self._get_image_properties() + self._image_mime_type = properties.get('type', + interfaces.nsISupportsCString).data + + return self._image_mime_type + + def get_image_name(self): + if not self.is_image(): + return None + + if not self._image_name: + properties = self._get_image_properties() + if properties.has('content-disposition'): + content_disposition = properties.get('content-disposition', + interfaces.nsISupportsCString).data + + cls = components.classes['@mozilla.org/network/mime-hdrparam;1'] + header_param = cls.getService(interfaces.nsIMIMEHeaderParam) + self._image_name = header_param.getParameter(content_disposition, + 'filename', '', True, None) + + if not self._image_name: + self._image_name = header_param.getParameter(content_disposition, + 'name', '', True, None) + + if not self._image_name: + url = urlparse.urlparse(self._image_element.src) + path = url[2] + self._image_name = os.path.split(path)[1] + + return self._image_name + + def save_image(self, file_path): + """ Based on nsWebBrowserPersist::OnDataAvailable: + http://lxr.mozilla.org/seamonkey/source/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp + """ + cls = components.classes['@mozilla.org/network/io-service;1'] + io_service = cls.getService(interfaces.nsIIOService) + uri = io_service.newURI(self._image_element.src, None, None) + + cls = components.classes['@mozilla.org/file/local;1'] + dest_file = cls.createInstance(interfaces.nsILocalFile) + dest_file.initWithPath(file_path) + + cls = components.classes['@mozilla.org/network/io-service;1'] + io_service = cls.getService(interfaces.nsIIOService) + input_channel = io_service.newChannelFromURI(uri) + + session_history = self._web_navigation.sessionHistory + entry = session_history.getEntryAtIndex(session_history.index, False) + entry = entry.queryInterface(interfaces.nsISHEntry) + post_data = entry.postData + if post_data: + http_channel = input_channel.queryInterface(interfaces.nsIHttpChannel) + stream = post_data.queryInterface(interfaces.nsISeekableStream) + stream.seek(interfaces.nsISeekableStream.NS_SEEK_SET, 0) + + upload_channel = http_channel.queryInterface(interfaces.nsIUploadChannel) + upload_channel.setUploadStream(post_data, '', -1) + + http_input_stream = input_channel.open() + + cls = components.classes['@mozilla.org/network/file-output-stream;1'] + file_output_stream = cls.createInstance(interfaces.nsIFileOutputStream) + file_output_stream.init(dest_file, -1, -1, 0) + + bytes_to_read = http_input_stream.available() + while bytes_to_read: + buf = str(http_input_stream.read(min(8192, bytes_to_read))) + bytes_read = len(buf) + bytes_to_write = bytes_read + while bytes_to_write: + bytes_written = file_output_stream.write(buf, bytes_read) + bytes_to_write -= bytes_written + + bytes_to_read -= bytes_read + + http_input_stream.close() + file_output_stream.close() + -- cgit v0.9.1