OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "ppapi/proxy/ppb_file_chooser_proxy.h" |
| 6 |
| 7 #include <queue> |
| 8 |
| 9 #include "ppapi/c/dev/ppb_file_chooser_dev.h" |
| 10 #include "ppapi/c/pp_errors.h" |
| 11 #include "ppapi/c/private/ppb_proxy_private.h" |
| 12 #include "ppapi/proxy/host_dispatcher.h" |
| 13 #include "ppapi/proxy/plugin_dispatcher.h" |
| 14 #include "ppapi/proxy/plugin_resource.h" |
| 15 #include "ppapi/proxy/ppapi_messages.h" |
| 16 #include "ppapi/proxy/ppb_file_ref_proxy.h" |
| 17 #include "ppapi/proxy/serialized_var.h" |
| 18 |
| 19 namespace pp { |
| 20 namespace proxy { |
| 21 |
| 22 class FileChooser : public PluginResource { |
| 23 public: |
| 24 FileChooser(const HostResource& resource); |
| 25 virtual ~FileChooser(); |
| 26 |
| 27 virtual FileChooser* AsFileChooser(); |
| 28 |
| 29 PP_CompletionCallback current_show_callback_; |
| 30 |
| 31 // All files returned by the current show callback that haven't yet been |
| 32 // given to the plugin. The plugin will repeatedly call us to get the next |
| 33 // file, and we'll vend those out of this queue, removing them when ownership |
| 34 // has transferred to the plugin. |
| 35 std::queue<PP_Resource> file_queue_; |
| 36 |
| 37 private: |
| 38 DISALLOW_COPY_AND_ASSIGN(FileChooser); |
| 39 }; |
| 40 |
| 41 FileChooser::FileChooser(const HostResource& resource) |
| 42 : PluginResource(resource), |
| 43 current_show_callback_(PP_MakeCompletionCallback(NULL, NULL)) { |
| 44 } |
| 45 |
| 46 FileChooser::~FileChooser() { |
| 47 // Always need to fire completion callbacks to prevent a leak in the plugin. |
| 48 if (current_show_callback_.func) |
| 49 PP_RunCompletionCallback(¤t_show_callback_, PP_ERROR_ABORTED); |
| 50 |
| 51 // Any existing files we haven't transferred ownership to the plugin need |
| 52 // to be freed. |
| 53 PluginResourceTracker* tracker = PluginResourceTracker::GetInstance(); |
| 54 while (!file_queue_.empty()) { |
| 55 tracker->ReleaseResource(file_queue_.front()); |
| 56 file_queue_.pop(); |
| 57 } |
| 58 } |
| 59 |
| 60 FileChooser* FileChooser::AsFileChooser() { |
| 61 return this; |
| 62 } |
| 63 |
| 64 namespace { |
| 65 |
| 66 PP_Resource Create(PP_Instance instance, |
| 67 const PP_FileChooserOptions_Dev* options) { |
| 68 Dispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); |
| 69 if (!dispatcher) |
| 70 return 0; |
| 71 |
| 72 HostResource result; |
| 73 dispatcher->Send(new PpapiHostMsg_PPBFileChooser_Create( |
| 74 INTERFACE_ID_PPB_FILE_CHOOSER, instance, |
| 75 static_cast<int>(options->mode), options->accept_mime_types, &result)); |
| 76 |
| 77 if (result.is_null()) |
| 78 return 0; |
| 79 linked_ptr<FileChooser> object(new FileChooser(result)); |
| 80 return PluginResourceTracker::GetInstance()->AddResource(object); |
| 81 } |
| 82 |
| 83 PP_Bool IsFileChooser(PP_Resource resource) { |
| 84 FileChooser* object = PluginResource::GetAs<FileChooser>(resource); |
| 85 return BoolToPPBool(!!object); |
| 86 } |
| 87 |
| 88 int32_t Show(PP_Resource chooser, struct PP_CompletionCallback callback) { |
| 89 FileChooser* object = PluginResource::GetAs<FileChooser>(chooser); |
| 90 if (!object) |
| 91 return PP_ERROR_BADRESOURCE; |
| 92 Dispatcher* dispatcher = PluginDispatcher::GetForInstance(object->instance()); |
| 93 if (!dispatcher) |
| 94 return PP_ERROR_BADARGUMENT; |
| 95 |
| 96 if (object->current_show_callback_.func) |
| 97 return PP_ERROR_INPROGRESS; // Can't show more than once. |
| 98 |
| 99 object->current_show_callback_ = callback; |
| 100 dispatcher->Send(new PpapiHostMsg_PPBFileChooser_Show( |
| 101 INTERFACE_ID_PPB_FILE_CHOOSER, |
| 102 object->host_resource())); |
| 103 return PP_ERROR_WOULDBLOCK; |
| 104 } |
| 105 |
| 106 PP_Resource GetNextChosenFile(PP_Resource chooser) { |
| 107 FileChooser* object = PluginResource::GetAs<FileChooser>(chooser); |
| 108 if (!object || object->file_queue_.empty()) |
| 109 return 0; |
| 110 |
| 111 // Return the next resource in the queue. These resource have already been |
| 112 // addrefed (they're currently owned by the FileChooser) and returning them |
| 113 // transfers ownership of that reference to the plugin. |
| 114 PP_Resource next = object->file_queue_.front(); |
| 115 object->file_queue_.pop(); |
| 116 return next; |
| 117 } |
| 118 |
| 119 const PPB_FileChooser_Dev file_chooser_interface = { |
| 120 &Create, |
| 121 &IsFileChooser, |
| 122 &Show, |
| 123 &GetNextChosenFile |
| 124 }; |
| 125 |
| 126 InterfaceProxy* CreateFileChooserProxy(Dispatcher* dispatcher, |
| 127 const void* target_interface) { |
| 128 return new PPB_FileChooser_Proxy(dispatcher, target_interface); |
| 129 } |
| 130 |
| 131 } // namespace |
| 132 |
| 133 PPB_FileChooser_Proxy::PPB_FileChooser_Proxy(Dispatcher* dispatcher, |
| 134 const void* target_interface) |
| 135 : InterfaceProxy(dispatcher, target_interface), |
| 136 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| 137 } |
| 138 |
| 139 PPB_FileChooser_Proxy::~PPB_FileChooser_Proxy() { |
| 140 } |
| 141 |
| 142 const InterfaceProxy::Info* PPB_FileChooser_Proxy::GetInfo() { |
| 143 static const Info info = { |
| 144 &file_chooser_interface, |
| 145 PPB_FILECHOOSER_DEV_INTERFACE, |
| 146 INTERFACE_ID_PPB_FILE_CHOOSER, |
| 147 false, |
| 148 &CreateFileChooserProxy, |
| 149 }; |
| 150 return &info; |
| 151 } |
| 152 |
| 153 bool PPB_FileChooser_Proxy::OnMessageReceived(const IPC::Message& msg) { |
| 154 bool handled = true; |
| 155 IPC_BEGIN_MESSAGE_MAP(PPB_FileChooser_Proxy, msg) |
| 156 // Plugin -> host messages. |
| 157 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFileChooser_Create, OnMsgCreate) |
| 158 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFileChooser_Show, OnMsgShow) |
| 159 |
| 160 // Host -> plugin messages. |
| 161 IPC_MESSAGE_HANDLER(PpapiMsg_PPBFileChooser_ChooseComplete, |
| 162 OnMsgChooseComplete) |
| 163 IPC_MESSAGE_UNHANDLED(handled = false) |
| 164 IPC_END_MESSAGE_MAP() |
| 165 return handled; |
| 166 } |
| 167 |
| 168 void PPB_FileChooser_Proxy::OnMsgCreate(PP_Instance instance, |
| 169 int mode, |
| 170 const std::string& accept_mime_types, |
| 171 HostResource* result) { |
| 172 PP_FileChooserOptions_Dev options; |
| 173 options.mode = static_cast<PP_FileChooserMode_Dev>(mode); |
| 174 options.accept_mime_types = accept_mime_types.c_str(); |
| 175 result->SetHostResource( |
| 176 instance, ppb_file_chooser_target()->Create(instance, &options)); |
| 177 } |
| 178 |
| 179 void PPB_FileChooser_Proxy::OnMsgShow(const HostResource& chooser) { |
| 180 CompletionCallback callback = callback_factory_.NewCallback( |
| 181 &PPB_FileChooser_Proxy::OnShowCallback, chooser); |
| 182 |
| 183 int32_t result = ppb_file_chooser_target()->Show( |
| 184 chooser.host_resource(), callback.pp_completion_callback()); |
| 185 if (result != PP_ERROR_WOULDBLOCK) |
| 186 callback.Run(result); |
| 187 } |
| 188 |
| 189 void PPB_FileChooser_Proxy::OnMsgChooseComplete( |
| 190 const HostResource& chooser, |
| 191 int32_t result_code, |
| 192 const std::vector<PPBFileRef_CreateInfo>& chosen_files) { |
| 193 PP_Resource plugin_resource = |
| 194 PluginResourceTracker::GetInstance()->PluginResourceForHostResource( |
| 195 chooser); |
| 196 if (!plugin_resource) |
| 197 return; |
| 198 FileChooser* object = PluginResource::GetAs<FileChooser>(plugin_resource); |
| 199 if (!object) |
| 200 return; |
| 201 |
| 202 // Convert each of the passed in file infos to resources. These will be owned |
| 203 // by the FileChooser object until they're passed to the plugin. |
| 204 DCHECK(object->file_queue_.empty()); |
| 205 for (size_t i = 0; i < chosen_files.size(); i++) { |
| 206 object->file_queue_.push( |
| 207 PPB_FileRef_Proxy::DeserializeFileRef(chosen_files[i])); |
| 208 } |
| 209 |
| 210 // Notify the plugin of the new data. We have to swap out the callback |
| 211 // because the plugin may trigger deleting the object from the callback, and |
| 212 // the FileChooser object will attempt to call the callback in its destructor |
| 213 // with the ABORTED status. |
| 214 PP_CompletionCallback callback = object->current_show_callback_; |
| 215 object->current_show_callback_ = PP_MakeCompletionCallback(NULL, NULL); |
| 216 PP_RunCompletionCallback(&callback, result_code); |
| 217 // DANGER: May delete |object|! |
| 218 } |
| 219 |
| 220 void PPB_FileChooser_Proxy::OnShowCallback(int32_t result, |
| 221 const HostResource& chooser) { |
| 222 std::vector<PPBFileRef_CreateInfo> files; |
| 223 if (result == PP_OK) { |
| 224 // Jump through some hoops to get the FileRef proxy. Since we know we're |
| 225 // in the host at this point, we can ask the host dispatcher for it. |
| 226 DCHECK(!dispatcher()->IsPlugin()); |
| 227 HostDispatcher* host_disp = static_cast<HostDispatcher*>(dispatcher()); |
| 228 PPB_FileRef_Proxy* file_ref_proxy = static_cast<PPB_FileRef_Proxy*>( |
| 229 host_disp->GetOrCreatePPBInterfaceProxy(INTERFACE_ID_PPB_FILE_REF)); |
| 230 |
| 231 // Convert the returned files to the serialized info. |
| 232 while (PP_Resource cur_file_resource = |
| 233 ppb_file_chooser_target()->GetNextChosenFile( |
| 234 chooser.host_resource())) { |
| 235 PPBFileRef_CreateInfo cur_create_info; |
| 236 file_ref_proxy->SerializeFileRef(cur_file_resource, &cur_create_info); |
| 237 files.push_back(cur_create_info); |
| 238 } |
| 239 } |
| 240 |
| 241 dispatcher()->Send(new PpapiMsg_PPBFileChooser_ChooseComplete( |
| 242 INTERFACE_ID_PPB_FILE_CHOOSER, chooser, result, files)); |
| 243 } |
| 244 |
| 245 } // namespace proxy |
| 246 } // namespace pp |
OLD | NEW |