Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "nsClipboard.h" |
michael@0 | 7 | #include <ole2.h> |
michael@0 | 8 | #include <shlobj.h> |
michael@0 | 9 | #include <intshcut.h> |
michael@0 | 10 | |
michael@0 | 11 | // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN |
michael@0 | 12 | #include <shellapi.h> |
michael@0 | 13 | |
michael@0 | 14 | #include "nsCOMPtr.h" |
michael@0 | 15 | #include "nsDataObj.h" |
michael@0 | 16 | #include "nsIClipboardOwner.h" |
michael@0 | 17 | #include "nsString.h" |
michael@0 | 18 | #include "nsNativeCharsetUtils.h" |
michael@0 | 19 | #include "nsIFormatConverter.h" |
michael@0 | 20 | #include "nsITransferable.h" |
michael@0 | 21 | #include "nsCOMPtr.h" |
michael@0 | 22 | #include "nsXPCOM.h" |
michael@0 | 23 | #include "nsISupportsPrimitives.h" |
michael@0 | 24 | #include "nsXPIDLString.h" |
michael@0 | 25 | #include "nsReadableUtils.h" |
michael@0 | 26 | #include "nsUnicharUtils.h" |
michael@0 | 27 | #include "nsPrimitiveHelpers.h" |
michael@0 | 28 | #include "nsImageClipboard.h" |
michael@0 | 29 | #include "nsIWidget.h" |
michael@0 | 30 | #include "nsIComponentManager.h" |
michael@0 | 31 | #include "nsWidgetsCID.h" |
michael@0 | 32 | #include "nsCRT.h" |
michael@0 | 33 | #include "nsNetUtil.h" |
michael@0 | 34 | #include "nsEscape.h" |
michael@0 | 35 | #include "nsIObserverService.h" |
michael@0 | 36 | |
michael@0 | 37 | #ifdef PR_LOGGING |
michael@0 | 38 | PRLogModuleInfo* gWin32ClipboardLog = nullptr; |
michael@0 | 39 | #endif |
michael@0 | 40 | |
michael@0 | 41 | // oddly, this isn't in the MSVC headers anywhere. |
michael@0 | 42 | UINT nsClipboard::CF_HTML = ::RegisterClipboardFormatW(L"HTML Format"); |
michael@0 | 43 | |
michael@0 | 44 | |
michael@0 | 45 | //------------------------------------------------------------------------- |
michael@0 | 46 | // |
michael@0 | 47 | // nsClipboard constructor |
michael@0 | 48 | // |
michael@0 | 49 | //------------------------------------------------------------------------- |
michael@0 | 50 | nsClipboard::nsClipboard() : nsBaseClipboard() |
michael@0 | 51 | { |
michael@0 | 52 | #ifdef PR_LOGGING |
michael@0 | 53 | if (!gWin32ClipboardLog) { |
michael@0 | 54 | gWin32ClipboardLog = PR_NewLogModule("nsClipboard"); |
michael@0 | 55 | } |
michael@0 | 56 | #endif |
michael@0 | 57 | |
michael@0 | 58 | mIgnoreEmptyNotification = false; |
michael@0 | 59 | mWindow = nullptr; |
michael@0 | 60 | |
michael@0 | 61 | // Register for a shutdown notification so that we can flush data |
michael@0 | 62 | // to the OS clipboard. |
michael@0 | 63 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 64 | do_GetService("@mozilla.org/observer-service;1"); |
michael@0 | 65 | if (observerService) |
michael@0 | 66 | observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE); |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | //------------------------------------------------------------------------- |
michael@0 | 70 | // nsClipboard destructor |
michael@0 | 71 | //------------------------------------------------------------------------- |
michael@0 | 72 | nsClipboard::~nsClipboard() |
michael@0 | 73 | { |
michael@0 | 74 | |
michael@0 | 75 | } |
michael@0 | 76 | |
michael@0 | 77 | NS_IMPL_ISUPPORTS_INHERITED(nsClipboard, nsBaseClipboard, nsIObserver) |
michael@0 | 78 | |
michael@0 | 79 | NS_IMETHODIMP |
michael@0 | 80 | nsClipboard::Observe(nsISupports *aSubject, const char *aTopic, |
michael@0 | 81 | const char16_t *aData) |
michael@0 | 82 | { |
michael@0 | 83 | // This will be called on shutdown. |
michael@0 | 84 | ::OleFlushClipboard(); |
michael@0 | 85 | ::CloseClipboard(); |
michael@0 | 86 | |
michael@0 | 87 | return NS_OK; |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | //------------------------------------------------------------------------- |
michael@0 | 91 | UINT nsClipboard::GetFormat(const char* aMimeStr) |
michael@0 | 92 | { |
michael@0 | 93 | UINT format; |
michael@0 | 94 | |
michael@0 | 95 | if (strcmp(aMimeStr, kTextMime) == 0) |
michael@0 | 96 | format = CF_TEXT; |
michael@0 | 97 | else if (strcmp(aMimeStr, kUnicodeMime) == 0) |
michael@0 | 98 | format = CF_UNICODETEXT; |
michael@0 | 99 | else if (strcmp(aMimeStr, kJPEGImageMime) == 0 || |
michael@0 | 100 | strcmp(aMimeStr, kJPGImageMime) == 0 || |
michael@0 | 101 | strcmp(aMimeStr, kPNGImageMime) == 0) |
michael@0 | 102 | format = CF_DIBV5; |
michael@0 | 103 | else if (strcmp(aMimeStr, kFileMime) == 0 || |
michael@0 | 104 | strcmp(aMimeStr, kFilePromiseMime) == 0) |
michael@0 | 105 | format = CF_HDROP; |
michael@0 | 106 | else if (strcmp(aMimeStr, kNativeHTMLMime) == 0) |
michael@0 | 107 | format = CF_HTML; |
michael@0 | 108 | else |
michael@0 | 109 | format = ::RegisterClipboardFormatW(NS_ConvertASCIItoUTF16(aMimeStr).get()); |
michael@0 | 110 | |
michael@0 | 111 | return format; |
michael@0 | 112 | } |
michael@0 | 113 | |
michael@0 | 114 | //------------------------------------------------------------------------- |
michael@0 | 115 | nsresult nsClipboard::CreateNativeDataObject(nsITransferable * aTransferable, IDataObject ** aDataObj, nsIURI * uri) |
michael@0 | 116 | { |
michael@0 | 117 | if (nullptr == aTransferable) { |
michael@0 | 118 | return NS_ERROR_FAILURE; |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | // Create our native DataObject that implements |
michael@0 | 122 | // the OLE IDataObject interface |
michael@0 | 123 | nsDataObj * dataObj = new nsDataObj(uri); |
michael@0 | 124 | |
michael@0 | 125 | if (!dataObj) |
michael@0 | 126 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 127 | |
michael@0 | 128 | dataObj->AddRef(); |
michael@0 | 129 | |
michael@0 | 130 | // Now set it up with all the right data flavors & enums |
michael@0 | 131 | nsresult res = SetupNativeDataObject(aTransferable, dataObj); |
michael@0 | 132 | if (NS_OK == res) { |
michael@0 | 133 | *aDataObj = dataObj; |
michael@0 | 134 | } else { |
michael@0 | 135 | delete dataObj; |
michael@0 | 136 | } |
michael@0 | 137 | return res; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | //------------------------------------------------------------------------- |
michael@0 | 141 | nsresult nsClipboard::SetupNativeDataObject(nsITransferable * aTransferable, IDataObject * aDataObj) |
michael@0 | 142 | { |
michael@0 | 143 | if (nullptr == aTransferable || nullptr == aDataObj) { |
michael@0 | 144 | return NS_ERROR_FAILURE; |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | nsDataObj * dObj = static_cast<nsDataObj *>(aDataObj); |
michael@0 | 148 | |
michael@0 | 149 | // Now give the Transferable to the DataObject |
michael@0 | 150 | // for getting the data out of it |
michael@0 | 151 | dObj->SetTransferable(aTransferable); |
michael@0 | 152 | |
michael@0 | 153 | // Get the transferable list of data flavors |
michael@0 | 154 | nsCOMPtr<nsISupportsArray> dfList; |
michael@0 | 155 | aTransferable->FlavorsTransferableCanExport(getter_AddRefs(dfList)); |
michael@0 | 156 | |
michael@0 | 157 | // Walk through flavors that contain data and register them |
michael@0 | 158 | // into the DataObj as supported flavors |
michael@0 | 159 | uint32_t i; |
michael@0 | 160 | uint32_t cnt; |
michael@0 | 161 | dfList->Count(&cnt); |
michael@0 | 162 | for (i=0;i<cnt;i++) { |
michael@0 | 163 | nsCOMPtr<nsISupports> genericFlavor; |
michael@0 | 164 | dfList->GetElementAt ( i, getter_AddRefs(genericFlavor) ); |
michael@0 | 165 | nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) ); |
michael@0 | 166 | if ( currentFlavor ) { |
michael@0 | 167 | nsXPIDLCString flavorStr; |
michael@0 | 168 | currentFlavor->ToString(getter_Copies(flavorStr)); |
michael@0 | 169 | UINT format = GetFormat(flavorStr); |
michael@0 | 170 | |
michael@0 | 171 | // Now tell the native IDataObject about both our mime type and |
michael@0 | 172 | // the native data format |
michael@0 | 173 | FORMATETC fe; |
michael@0 | 174 | SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL); |
michael@0 | 175 | dObj->AddDataFlavor(flavorStr, &fe); |
michael@0 | 176 | |
michael@0 | 177 | // Do various things internal to the implementation, like map one |
michael@0 | 178 | // flavor to another or add additional flavors based on what's required |
michael@0 | 179 | // for the win32 impl. |
michael@0 | 180 | if ( strcmp(flavorStr, kUnicodeMime) == 0 ) { |
michael@0 | 181 | // if we find text/unicode, also advertise text/plain (which we will convert |
michael@0 | 182 | // on our own in nsDataObj::GetText(). |
michael@0 | 183 | FORMATETC textFE; |
michael@0 | 184 | SET_FORMATETC(textFE, CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL); |
michael@0 | 185 | dObj->AddDataFlavor(kTextMime, &textFE); |
michael@0 | 186 | } |
michael@0 | 187 | else if ( strcmp(flavorStr, kHTMLMime) == 0 ) { |
michael@0 | 188 | // if we find text/html, also advertise win32's html flavor (which we will convert |
michael@0 | 189 | // on our own in nsDataObj::GetText(). |
michael@0 | 190 | FORMATETC htmlFE; |
michael@0 | 191 | SET_FORMATETC(htmlFE, CF_HTML, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL); |
michael@0 | 192 | dObj->AddDataFlavor(kHTMLMime, &htmlFE); |
michael@0 | 193 | } |
michael@0 | 194 | else if ( strcmp(flavorStr, kURLMime) == 0 ) { |
michael@0 | 195 | // if we're a url, in addition to also being text, we need to register |
michael@0 | 196 | // the "file" flavors so that the win32 shell knows to create an internet |
michael@0 | 197 | // shortcut when it sees one of these beasts. |
michael@0 | 198 | FORMATETC shortcutFE; |
michael@0 | 199 | SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) |
michael@0 | 200 | dObj->AddDataFlavor(kURLMime, &shortcutFE); |
michael@0 | 201 | SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) |
michael@0 | 202 | dObj->AddDataFlavor(kURLMime, &shortcutFE); |
michael@0 | 203 | SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) |
michael@0 | 204 | dObj->AddDataFlavor(kURLMime, &shortcutFE); |
michael@0 | 205 | SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLA), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) |
michael@0 | 206 | dObj->AddDataFlavor(kURLMime, &shortcutFE); |
michael@0 | 207 | SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLW), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) |
michael@0 | 208 | dObj->AddDataFlavor(kURLMime, &shortcutFE); |
michael@0 | 209 | } |
michael@0 | 210 | else if ( strcmp(flavorStr, kPNGImageMime) == 0 || strcmp(flavorStr, kJPEGImageMime) == 0 || |
michael@0 | 211 | strcmp(flavorStr, kJPGImageMime) == 0 || strcmp(flavorStr, kGIFImageMime) == 0 || |
michael@0 | 212 | strcmp(flavorStr, kNativeImageMime) == 0 ) { |
michael@0 | 213 | // if we're an image, register the native bitmap flavor |
michael@0 | 214 | FORMATETC imageFE; |
michael@0 | 215 | // Add DIBv5 |
michael@0 | 216 | SET_FORMATETC(imageFE, CF_DIBV5, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) |
michael@0 | 217 | dObj->AddDataFlavor(flavorStr, &imageFE); |
michael@0 | 218 | // Add DIBv3 |
michael@0 | 219 | SET_FORMATETC(imageFE, CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) |
michael@0 | 220 | dObj->AddDataFlavor(flavorStr, &imageFE); |
michael@0 | 221 | } |
michael@0 | 222 | else if ( strcmp(flavorStr, kFilePromiseMime) == 0 ) { |
michael@0 | 223 | // if we're a file promise flavor, also register the |
michael@0 | 224 | // CFSTR_PREFERREDDROPEFFECT format. The data object |
michael@0 | 225 | // returns a value of DROPEFFECTS_MOVE to the drop target |
michael@0 | 226 | // when it asks for the value of this format. This causes |
michael@0 | 227 | // the file to be moved from the temporary location instead |
michael@0 | 228 | // of being copied. The right thing to do here is to call |
michael@0 | 229 | // SetData() on the data object and set the value of this format |
michael@0 | 230 | // to DROPEFFECTS_MOVE on this particular data object. But, |
michael@0 | 231 | // since all the other clipboard formats follow the model of setting |
michael@0 | 232 | // data on the data object only when the drop object calls GetData(), |
michael@0 | 233 | // I am leaving this format's value hard coded in the data object. |
michael@0 | 234 | // We can change this if other consumers of this format get added to this |
michael@0 | 235 | // codebase and they need different values. |
michael@0 | 236 | FORMATETC shortcutFE; |
michael@0 | 237 | SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) |
michael@0 | 238 | dObj->AddDataFlavor(kFilePromiseMime, &shortcutFE); |
michael@0 | 239 | } |
michael@0 | 240 | } |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | return NS_OK; |
michael@0 | 244 | } |
michael@0 | 245 | |
michael@0 | 246 | //------------------------------------------------------------------------- |
michael@0 | 247 | NS_IMETHODIMP nsClipboard::SetNativeClipboardData ( int32_t aWhichClipboard ) |
michael@0 | 248 | { |
michael@0 | 249 | if ( aWhichClipboard != kGlobalClipboard ) |
michael@0 | 250 | return NS_ERROR_FAILURE; |
michael@0 | 251 | |
michael@0 | 252 | mIgnoreEmptyNotification = true; |
michael@0 | 253 | |
michael@0 | 254 | // make sure we have a good transferable |
michael@0 | 255 | if (nullptr == mTransferable) { |
michael@0 | 256 | return NS_ERROR_FAILURE; |
michael@0 | 257 | } |
michael@0 | 258 | |
michael@0 | 259 | IDataObject * dataObj; |
michael@0 | 260 | if ( NS_SUCCEEDED(CreateNativeDataObject(mTransferable, &dataObj, nullptr)) ) { // this add refs dataObj |
michael@0 | 261 | ::OleSetClipboard(dataObj); |
michael@0 | 262 | dataObj->Release(); |
michael@0 | 263 | } else { |
michael@0 | 264 | // Clear the native clipboard |
michael@0 | 265 | ::OleSetClipboard(nullptr); |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | mIgnoreEmptyNotification = false; |
michael@0 | 269 | |
michael@0 | 270 | return NS_OK; |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | |
michael@0 | 274 | //------------------------------------------------------------------------- |
michael@0 | 275 | nsresult nsClipboard::GetGlobalData(HGLOBAL aHGBL, void ** aData, uint32_t * aLen) |
michael@0 | 276 | { |
michael@0 | 277 | // Allocate a new memory buffer and copy the data from global memory. |
michael@0 | 278 | // Recall that win98 allocates to nearest DWORD boundary. As a safety |
michael@0 | 279 | // precaution, allocate an extra 2 bytes (but don't report them!) and |
michael@0 | 280 | // null them out to ensure that all of our strlen calls will succeed. |
michael@0 | 281 | nsresult result = NS_ERROR_FAILURE; |
michael@0 | 282 | if (aHGBL != nullptr) { |
michael@0 | 283 | LPSTR lpStr = (LPSTR) GlobalLock(aHGBL); |
michael@0 | 284 | DWORD allocSize = GlobalSize(aHGBL); |
michael@0 | 285 | char* data = static_cast<char*>(nsMemory::Alloc(allocSize + sizeof(char16_t))); |
michael@0 | 286 | if ( data ) { |
michael@0 | 287 | memcpy ( data, lpStr, allocSize ); |
michael@0 | 288 | data[allocSize] = data[allocSize + 1] = '\0'; // null terminate for safety |
michael@0 | 289 | |
michael@0 | 290 | GlobalUnlock(aHGBL); |
michael@0 | 291 | *aData = data; |
michael@0 | 292 | *aLen = allocSize; |
michael@0 | 293 | |
michael@0 | 294 | result = NS_OK; |
michael@0 | 295 | } |
michael@0 | 296 | } else { |
michael@0 | 297 | #ifdef MOZ_METRO |
michael@0 | 298 | return result; |
michael@0 | 299 | #endif |
michael@0 | 300 | // We really shouldn't ever get here |
michael@0 | 301 | // but just in case |
michael@0 | 302 | *aData = nullptr; |
michael@0 | 303 | *aLen = 0; |
michael@0 | 304 | LPVOID lpMsgBuf; |
michael@0 | 305 | |
michael@0 | 306 | FormatMessageW( |
michael@0 | 307 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, |
michael@0 | 308 | nullptr, |
michael@0 | 309 | GetLastError(), |
michael@0 | 310 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language |
michael@0 | 311 | (LPWSTR) &lpMsgBuf, |
michael@0 | 312 | 0, |
michael@0 | 313 | nullptr |
michael@0 | 314 | ); |
michael@0 | 315 | |
michael@0 | 316 | // Display the string. |
michael@0 | 317 | MessageBoxW( nullptr, (LPCWSTR) lpMsgBuf, L"GetLastError", |
michael@0 | 318 | MB_OK | MB_ICONINFORMATION ); |
michael@0 | 319 | |
michael@0 | 320 | // Free the buffer. |
michael@0 | 321 | LocalFree( lpMsgBuf ); |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | return result; |
michael@0 | 325 | } |
michael@0 | 326 | |
michael@0 | 327 | //------------------------------------------------------------------------- |
michael@0 | 328 | nsresult nsClipboard::GetNativeDataOffClipboard(nsIWidget * aWidget, UINT /*aIndex*/, UINT aFormat, void ** aData, uint32_t * aLen) |
michael@0 | 329 | { |
michael@0 | 330 | HGLOBAL hglb; |
michael@0 | 331 | nsresult result = NS_ERROR_FAILURE; |
michael@0 | 332 | |
michael@0 | 333 | HWND nativeWin = nullptr; |
michael@0 | 334 | if (::OpenClipboard(nativeWin)) { |
michael@0 | 335 | hglb = ::GetClipboardData(aFormat); |
michael@0 | 336 | result = GetGlobalData(hglb, aData, aLen); |
michael@0 | 337 | ::CloseClipboard(); |
michael@0 | 338 | } |
michael@0 | 339 | return result; |
michael@0 | 340 | } |
michael@0 | 341 | |
michael@0 | 342 | static void DisplayErrCode(HRESULT hres) |
michael@0 | 343 | { |
michael@0 | 344 | #if defined(DEBUG_rods) || defined(DEBUG_pinkerton) |
michael@0 | 345 | if (hres == E_INVALIDARG) { |
michael@0 | 346 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("E_INVALIDARG\n")); |
michael@0 | 347 | } else |
michael@0 | 348 | if (hres == E_UNEXPECTED) { |
michael@0 | 349 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("E_UNEXPECTED\n")); |
michael@0 | 350 | } else |
michael@0 | 351 | if (hres == E_OUTOFMEMORY) { |
michael@0 | 352 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("E_OUTOFMEMORY\n")); |
michael@0 | 353 | } else |
michael@0 | 354 | if (hres == DV_E_LINDEX ) { |
michael@0 | 355 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_LINDEX\n")); |
michael@0 | 356 | } else |
michael@0 | 357 | if (hres == DV_E_FORMATETC) { |
michael@0 | 358 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_FORMATETC\n")); |
michael@0 | 359 | } else |
michael@0 | 360 | if (hres == DV_E_TYMED) { |
michael@0 | 361 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_TYMED\n")); |
michael@0 | 362 | } else |
michael@0 | 363 | if (hres == DV_E_DVASPECT) { |
michael@0 | 364 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_DVASPECT\n")); |
michael@0 | 365 | } else |
michael@0 | 366 | if (hres == OLE_E_NOTRUNNING) { |
michael@0 | 367 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("OLE_E_NOTRUNNING\n")); |
michael@0 | 368 | } else |
michael@0 | 369 | if (hres == STG_E_MEDIUMFULL) { |
michael@0 | 370 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("STG_E_MEDIUMFULL\n")); |
michael@0 | 371 | } else |
michael@0 | 372 | if (hres == DV_E_CLIPFORMAT) { |
michael@0 | 373 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_CLIPFORMAT\n")); |
michael@0 | 374 | } else |
michael@0 | 375 | if (hres == S_OK) { |
michael@0 | 376 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("S_OK\n")); |
michael@0 | 377 | } else { |
michael@0 | 378 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, |
michael@0 | 379 | ("****** DisplayErrCode 0x%X\n", hres)); |
michael@0 | 380 | } |
michael@0 | 381 | #endif |
michael@0 | 382 | } |
michael@0 | 383 | |
michael@0 | 384 | //------------------------------------------------------------------------- |
michael@0 | 385 | static HRESULT FillSTGMedium(IDataObject * aDataObject, UINT aFormat, LPFORMATETC pFE, LPSTGMEDIUM pSTM, DWORD aTymed) |
michael@0 | 386 | { |
michael@0 | 387 | SET_FORMATETC(*pFE, aFormat, 0, DVASPECT_CONTENT, -1, aTymed); |
michael@0 | 388 | |
michael@0 | 389 | // Starting by querying for the data to see if we can get it as from global memory |
michael@0 | 390 | HRESULT hres = S_FALSE; |
michael@0 | 391 | hres = aDataObject->QueryGetData(pFE); |
michael@0 | 392 | DisplayErrCode(hres); |
michael@0 | 393 | if (S_OK == hres) { |
michael@0 | 394 | hres = aDataObject->GetData(pFE, pSTM); |
michael@0 | 395 | DisplayErrCode(hres); |
michael@0 | 396 | } |
michael@0 | 397 | return hres; |
michael@0 | 398 | } |
michael@0 | 399 | |
michael@0 | 400 | |
michael@0 | 401 | //------------------------------------------------------------------------- |
michael@0 | 402 | // If aFormat is CF_DIBV5, aMIMEImageFormat must be a type for which we have |
michael@0 | 403 | // an image encoder (e.g. image/png). |
michael@0 | 404 | // For other values of aFormat, it is OK to pass null for aMIMEImageFormat. |
michael@0 | 405 | nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject * aDataObject, UINT aIndex, UINT aFormat, const char * aMIMEImageFormat, void ** aData, uint32_t * aLen) |
michael@0 | 406 | { |
michael@0 | 407 | nsresult result = NS_ERROR_FAILURE; |
michael@0 | 408 | *aData = nullptr; |
michael@0 | 409 | *aLen = 0; |
michael@0 | 410 | |
michael@0 | 411 | if ( !aDataObject ) |
michael@0 | 412 | return result; |
michael@0 | 413 | |
michael@0 | 414 | UINT format = aFormat; |
michael@0 | 415 | HRESULT hres = S_FALSE; |
michael@0 | 416 | |
michael@0 | 417 | // XXX at the moment we only support global memory transfers |
michael@0 | 418 | // It is here where we will add support for native images |
michael@0 | 419 | // and IStream |
michael@0 | 420 | FORMATETC fe; |
michael@0 | 421 | STGMEDIUM stm; |
michael@0 | 422 | hres = FillSTGMedium(aDataObject, format, &fe, &stm, TYMED_HGLOBAL); |
michael@0 | 423 | |
michael@0 | 424 | // Currently this is only handling TYMED_HGLOBAL data |
michael@0 | 425 | // For Text, Dibs, Files, and generic data (like HTML) |
michael@0 | 426 | if (S_OK == hres) { |
michael@0 | 427 | static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA ); |
michael@0 | 428 | static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW ); |
michael@0 | 429 | static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS ); |
michael@0 | 430 | static CLIPFORMAT preferredDropEffect = ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT); |
michael@0 | 431 | |
michael@0 | 432 | switch (stm.tymed) { |
michael@0 | 433 | case TYMED_HGLOBAL: |
michael@0 | 434 | { |
michael@0 | 435 | switch (fe.cfFormat) { |
michael@0 | 436 | case CF_TEXT: |
michael@0 | 437 | { |
michael@0 | 438 | // Get the data out of the global data handle. The size we return |
michael@0 | 439 | // should not include the null because the other platforms don't |
michael@0 | 440 | // use nulls, so just return the length we get back from strlen(), |
michael@0 | 441 | // since we know CF_TEXT is null terminated. Recall that GetGlobalData() |
michael@0 | 442 | // returns the size of the allocated buffer, not the size of the data |
michael@0 | 443 | // (on 98, these are not the same) so we can't use that. |
michael@0 | 444 | uint32_t allocLen = 0; |
michael@0 | 445 | if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) { |
michael@0 | 446 | *aLen = strlen ( reinterpret_cast<char*>(*aData) ); |
michael@0 | 447 | result = NS_OK; |
michael@0 | 448 | } |
michael@0 | 449 | } break; |
michael@0 | 450 | |
michael@0 | 451 | case CF_UNICODETEXT: |
michael@0 | 452 | { |
michael@0 | 453 | // Get the data out of the global data handle. The size we return |
michael@0 | 454 | // should not include the null because the other platforms don't |
michael@0 | 455 | // use nulls, so just return the length we get back from strlen(), |
michael@0 | 456 | // since we know CF_UNICODETEXT is null terminated. Recall that GetGlobalData() |
michael@0 | 457 | // returns the size of the allocated buffer, not the size of the data |
michael@0 | 458 | // (on 98, these are not the same) so we can't use that. |
michael@0 | 459 | uint32_t allocLen = 0; |
michael@0 | 460 | if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) { |
michael@0 | 461 | *aLen = NS_strlen(reinterpret_cast<char16_t*>(*aData)) * 2; |
michael@0 | 462 | result = NS_OK; |
michael@0 | 463 | } |
michael@0 | 464 | } break; |
michael@0 | 465 | |
michael@0 | 466 | case CF_DIBV5: |
michael@0 | 467 | if (aMIMEImageFormat) |
michael@0 | 468 | { |
michael@0 | 469 | uint32_t allocLen = 0; |
michael@0 | 470 | unsigned char * clipboardData; |
michael@0 | 471 | if (NS_SUCCEEDED(GetGlobalData(stm.hGlobal, (void **)&clipboardData, &allocLen))) |
michael@0 | 472 | { |
michael@0 | 473 | nsImageFromClipboard converter; |
michael@0 | 474 | nsIInputStream * inputStream; |
michael@0 | 475 | converter.GetEncodedImageStream(clipboardData, aMIMEImageFormat, &inputStream); // addrefs for us, don't release |
michael@0 | 476 | if ( inputStream ) { |
michael@0 | 477 | *aData = inputStream; |
michael@0 | 478 | *aLen = sizeof(nsIInputStream*); |
michael@0 | 479 | result = NS_OK; |
michael@0 | 480 | } |
michael@0 | 481 | } |
michael@0 | 482 | } break; |
michael@0 | 483 | |
michael@0 | 484 | case CF_HDROP : |
michael@0 | 485 | { |
michael@0 | 486 | // in the case of a file drop, multiple files are stashed within a |
michael@0 | 487 | // single data object. In order to match mozilla's D&D apis, we |
michael@0 | 488 | // just pull out the file at the requested index, pretending as |
michael@0 | 489 | // if there really are multiple drag items. |
michael@0 | 490 | HDROP dropFiles = (HDROP) GlobalLock(stm.hGlobal); |
michael@0 | 491 | |
michael@0 | 492 | UINT numFiles = ::DragQueryFileW(dropFiles, 0xFFFFFFFF, nullptr, 0); |
michael@0 | 493 | NS_ASSERTION ( numFiles > 0, "File drop flavor, but no files...hmmmm" ); |
michael@0 | 494 | NS_ASSERTION ( aIndex < numFiles, "Asked for a file index out of range of list" ); |
michael@0 | 495 | if (numFiles > 0) { |
michael@0 | 496 | UINT fileNameLen = ::DragQueryFileW(dropFiles, aIndex, nullptr, 0); |
michael@0 | 497 | wchar_t* buffer = reinterpret_cast<wchar_t*>(nsMemory::Alloc((fileNameLen + 1) * sizeof(wchar_t))); |
michael@0 | 498 | if ( buffer ) { |
michael@0 | 499 | ::DragQueryFileW(dropFiles, aIndex, buffer, fileNameLen + 1); |
michael@0 | 500 | *aData = buffer; |
michael@0 | 501 | *aLen = fileNameLen * sizeof(char16_t); |
michael@0 | 502 | result = NS_OK; |
michael@0 | 503 | } |
michael@0 | 504 | else |
michael@0 | 505 | result = NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 506 | } |
michael@0 | 507 | GlobalUnlock (stm.hGlobal) ; |
michael@0 | 508 | |
michael@0 | 509 | } break; |
michael@0 | 510 | |
michael@0 | 511 | default: { |
michael@0 | 512 | if ( fe.cfFormat == fileDescriptorFlavorA || fe.cfFormat == fileDescriptorFlavorW || fe.cfFormat == fileFlavor ) { |
michael@0 | 513 | NS_WARNING ( "Mozilla doesn't yet understand how to read this type of file flavor" ); |
michael@0 | 514 | } |
michael@0 | 515 | else |
michael@0 | 516 | { |
michael@0 | 517 | // Get the data out of the global data handle. The size we return |
michael@0 | 518 | // should not include the null because the other platforms don't |
michael@0 | 519 | // use nulls, so just return the length we get back from strlen(), |
michael@0 | 520 | // since we know CF_UNICODETEXT is null terminated. Recall that GetGlobalData() |
michael@0 | 521 | // returns the size of the allocated buffer, not the size of the data |
michael@0 | 522 | // (on 98, these are not the same) so we can't use that. |
michael@0 | 523 | // |
michael@0 | 524 | // NOTE: we are assuming that anything that falls into this default case |
michael@0 | 525 | // is unicode. As we start to get more kinds of binary data, this |
michael@0 | 526 | // may become an incorrect assumption. Stay tuned. |
michael@0 | 527 | uint32_t allocLen = 0; |
michael@0 | 528 | if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) { |
michael@0 | 529 | if ( fe.cfFormat == CF_HTML ) { |
michael@0 | 530 | // CF_HTML is actually UTF8, not unicode, so disregard the assumption |
michael@0 | 531 | // above. We have to check the header for the actual length, and we'll |
michael@0 | 532 | // do that in FindPlatformHTML(). For now, return the allocLen. This |
michael@0 | 533 | // case is mostly to ensure we don't try to call strlen on the buffer. |
michael@0 | 534 | *aLen = allocLen; |
michael@0 | 535 | } else if (fe.cfFormat == preferredDropEffect) { |
michael@0 | 536 | // As per the MSDN doc entitled: "Shell Clipboard Formats" |
michael@0 | 537 | // CFSTR_PREFERREDDROPEFFECT should return a DWORD |
michael@0 | 538 | // Reference: http://msdn.microsoft.com/en-us/library/bb776902(v=vs.85).aspx |
michael@0 | 539 | NS_ASSERTION(allocLen == sizeof(DWORD), |
michael@0 | 540 | "CFSTR_PREFERREDDROPEFFECT should return a DWORD"); |
michael@0 | 541 | *aLen = allocLen; |
michael@0 | 542 | } else { |
michael@0 | 543 | *aLen = NS_strlen(reinterpret_cast<char16_t*>(*aData)) * |
michael@0 | 544 | sizeof(char16_t); |
michael@0 | 545 | } |
michael@0 | 546 | result = NS_OK; |
michael@0 | 547 | } |
michael@0 | 548 | } |
michael@0 | 549 | } break; |
michael@0 | 550 | } // switch |
michael@0 | 551 | } break; |
michael@0 | 552 | |
michael@0 | 553 | case TYMED_GDI: |
michael@0 | 554 | { |
michael@0 | 555 | #ifdef DEBUG |
michael@0 | 556 | PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, |
michael@0 | 557 | ("*********************** TYMED_GDI\n")); |
michael@0 | 558 | #endif |
michael@0 | 559 | } break; |
michael@0 | 560 | |
michael@0 | 561 | default: |
michael@0 | 562 | break; |
michael@0 | 563 | } //switch |
michael@0 | 564 | |
michael@0 | 565 | ReleaseStgMedium(&stm); |
michael@0 | 566 | } |
michael@0 | 567 | |
michael@0 | 568 | return result; |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | |
michael@0 | 572 | //------------------------------------------------------------------------- |
michael@0 | 573 | nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject, |
michael@0 | 574 | UINT anIndex, |
michael@0 | 575 | nsIWidget * aWindow, |
michael@0 | 576 | nsITransferable * aTransferable) |
michael@0 | 577 | { |
michael@0 | 578 | // make sure we have a good transferable |
michael@0 | 579 | if ( !aTransferable ) |
michael@0 | 580 | return NS_ERROR_INVALID_ARG; |
michael@0 | 581 | |
michael@0 | 582 | nsresult res = NS_ERROR_FAILURE; |
michael@0 | 583 | |
michael@0 | 584 | // get flavor list that includes all flavors that can be written (including ones |
michael@0 | 585 | // obtained through conversion) |
michael@0 | 586 | nsCOMPtr<nsISupportsArray> flavorList; |
michael@0 | 587 | res = aTransferable->FlavorsTransferableCanImport ( getter_AddRefs(flavorList) ); |
michael@0 | 588 | if ( NS_FAILED(res) ) |
michael@0 | 589 | return NS_ERROR_FAILURE; |
michael@0 | 590 | |
michael@0 | 591 | // Walk through flavors and see which flavor is on the clipboard them on the native clipboard, |
michael@0 | 592 | uint32_t i; |
michael@0 | 593 | uint32_t cnt; |
michael@0 | 594 | flavorList->Count(&cnt); |
michael@0 | 595 | for (i=0;i<cnt;i++) { |
michael@0 | 596 | nsCOMPtr<nsISupports> genericFlavor; |
michael@0 | 597 | flavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) ); |
michael@0 | 598 | nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) ); |
michael@0 | 599 | if ( currentFlavor ) { |
michael@0 | 600 | nsXPIDLCString flavorStr; |
michael@0 | 601 | currentFlavor->ToString(getter_Copies(flavorStr)); |
michael@0 | 602 | UINT format = GetFormat(flavorStr); |
michael@0 | 603 | |
michael@0 | 604 | // Try to get the data using the desired flavor. This might fail, but all is |
michael@0 | 605 | // not lost. |
michael@0 | 606 | void* data = nullptr; |
michael@0 | 607 | uint32_t dataLen = 0; |
michael@0 | 608 | bool dataFound = false; |
michael@0 | 609 | if (nullptr != aDataObject) { |
michael@0 | 610 | if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject, anIndex, format, flavorStr, &data, &dataLen)) ) |
michael@0 | 611 | dataFound = true; |
michael@0 | 612 | } |
michael@0 | 613 | else if (nullptr != aWindow) { |
michael@0 | 614 | if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow, anIndex, format, &data, &dataLen)) ) |
michael@0 | 615 | dataFound = true; |
michael@0 | 616 | } |
michael@0 | 617 | |
michael@0 | 618 | // This is our second chance to try to find some data, having not found it |
michael@0 | 619 | // when directly asking for the flavor. Let's try digging around in other |
michael@0 | 620 | // flavors to help satisfy our craving for data. |
michael@0 | 621 | if ( !dataFound ) { |
michael@0 | 622 | if ( strcmp(flavorStr, kUnicodeMime) == 0 ) |
michael@0 | 623 | dataFound = FindUnicodeFromPlainText ( aDataObject, anIndex, &data, &dataLen ); |
michael@0 | 624 | else if ( strcmp(flavorStr, kURLMime) == 0 ) { |
michael@0 | 625 | // drags from other windows apps expose the native |
michael@0 | 626 | // CFSTR_INETURL{A,W} flavor |
michael@0 | 627 | dataFound = FindURLFromNativeURL ( aDataObject, anIndex, &data, &dataLen ); |
michael@0 | 628 | if ( !dataFound ) |
michael@0 | 629 | dataFound = FindURLFromLocalFile ( aDataObject, anIndex, &data, &dataLen ); |
michael@0 | 630 | } |
michael@0 | 631 | } // if we try one last ditch effort to find our data |
michael@0 | 632 | |
michael@0 | 633 | // Hopefully by this point we've found it and can go about our business |
michael@0 | 634 | if ( dataFound ) { |
michael@0 | 635 | nsCOMPtr<nsISupports> genericDataWrapper; |
michael@0 | 636 | if ( strcmp(flavorStr, kFileMime) == 0 ) { |
michael@0 | 637 | // we have a file path in |data|. Create an nsLocalFile object. |
michael@0 | 638 | nsDependentString filepath(reinterpret_cast<char16_t*>(data)); |
michael@0 | 639 | nsCOMPtr<nsIFile> file; |
michael@0 | 640 | if ( NS_SUCCEEDED(NS_NewLocalFile(filepath, false, getter_AddRefs(file))) ) |
michael@0 | 641 | genericDataWrapper = do_QueryInterface(file); |
michael@0 | 642 | nsMemory::Free(data); |
michael@0 | 643 | } |
michael@0 | 644 | else if ( strcmp(flavorStr, kNativeHTMLMime) == 0) { |
michael@0 | 645 | // the editor folks want CF_HTML exactly as it's on the clipboard, no conversions, |
michael@0 | 646 | // no fancy stuff. Pull it off the clipboard, stuff it into a wrapper and hand |
michael@0 | 647 | // it back to them. |
michael@0 | 648 | if ( FindPlatformHTML(aDataObject, anIndex, &data, &dataLen) ) |
michael@0 | 649 | nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper) ); |
michael@0 | 650 | else |
michael@0 | 651 | { |
michael@0 | 652 | nsMemory::Free(data); |
michael@0 | 653 | continue; // something wrong with this flavor, keep looking for other data |
michael@0 | 654 | } |
michael@0 | 655 | nsMemory::Free(data); |
michael@0 | 656 | } |
michael@0 | 657 | else if ( strcmp(flavorStr, kJPEGImageMime) == 0 || |
michael@0 | 658 | strcmp(flavorStr, kJPGImageMime) == 0 || |
michael@0 | 659 | strcmp(flavorStr, kPNGImageMime) == 0) { |
michael@0 | 660 | nsIInputStream * imageStream = reinterpret_cast<nsIInputStream*>(data); |
michael@0 | 661 | genericDataWrapper = do_QueryInterface(imageStream); |
michael@0 | 662 | NS_IF_RELEASE(imageStream); |
michael@0 | 663 | } |
michael@0 | 664 | else { |
michael@0 | 665 | // we probably have some form of text. The DOM only wants LF, so convert from Win32 line |
michael@0 | 666 | // endings to DOM line endings. |
michael@0 | 667 | int32_t signedLen = static_cast<int32_t>(dataLen); |
michael@0 | 668 | nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks ( flavorStr, &data, &signedLen ); |
michael@0 | 669 | dataLen = signedLen; |
michael@0 | 670 | |
michael@0 | 671 | nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper) ); |
michael@0 | 672 | nsMemory::Free(data); |
michael@0 | 673 | } |
michael@0 | 674 | |
michael@0 | 675 | NS_ASSERTION ( genericDataWrapper, "About to put null data into the transferable" ); |
michael@0 | 676 | aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLen); |
michael@0 | 677 | res = NS_OK; |
michael@0 | 678 | |
michael@0 | 679 | // we found one, get out of the loop |
michael@0 | 680 | break; |
michael@0 | 681 | } |
michael@0 | 682 | |
michael@0 | 683 | } |
michael@0 | 684 | } // foreach flavor |
michael@0 | 685 | |
michael@0 | 686 | return res; |
michael@0 | 687 | |
michael@0 | 688 | } |
michael@0 | 689 | |
michael@0 | 690 | |
michael@0 | 691 | |
michael@0 | 692 | // |
michael@0 | 693 | // FindPlatformHTML |
michael@0 | 694 | // |
michael@0 | 695 | // Someone asked for the OS CF_HTML flavor. We give it back to them exactly as-is. |
michael@0 | 696 | // |
michael@0 | 697 | bool |
michael@0 | 698 | nsClipboard :: FindPlatformHTML ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen ) |
michael@0 | 699 | { |
michael@0 | 700 | // Reference: MSDN doc entitled "HTML Clipboard Format" |
michael@0 | 701 | // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854 |
michael@0 | 702 | // CF_HTML is UTF8, not unicode. We also can't rely on it being null-terminated |
michael@0 | 703 | // so we have to check the CF_HTML header for the correct length. |
michael@0 | 704 | // The length we return is the bytecount from the beginning of the selected data to the end |
michael@0 | 705 | // of the selected data, without the null termination. Because it's UTF8, we're guaranteed |
michael@0 | 706 | // the header is ASCII. |
michael@0 | 707 | |
michael@0 | 708 | if (!outData || !*outData) { |
michael@0 | 709 | return false; |
michael@0 | 710 | } |
michael@0 | 711 | |
michael@0 | 712 | char version[8] = { 0 }; |
michael@0 | 713 | int32_t startOfData = 0; |
michael@0 | 714 | int32_t endOfData = 0; |
michael@0 | 715 | int numFound = sscanf((char*)*outData, "Version:%7s\nStartHTML:%d\nEndHTML:%d", |
michael@0 | 716 | version, &startOfData, &endOfData); |
michael@0 | 717 | |
michael@0 | 718 | if (numFound != 3 || startOfData < -1 || endOfData < -1) { |
michael@0 | 719 | return false; |
michael@0 | 720 | } |
michael@0 | 721 | |
michael@0 | 722 | // Fixup the start and end markers if they have no context (set to -1) |
michael@0 | 723 | if (startOfData == -1) { |
michael@0 | 724 | startOfData = 0; |
michael@0 | 725 | } |
michael@0 | 726 | if (endOfData == -1) { |
michael@0 | 727 | endOfData = *outDataLen; |
michael@0 | 728 | } |
michael@0 | 729 | |
michael@0 | 730 | // Make sure we were passed sane values within our buffer size. |
michael@0 | 731 | // (Note that we've handled all cases of negative endOfData above, so we can |
michael@0 | 732 | // safely cast it to be unsigned here.) |
michael@0 | 733 | if (!endOfData || startOfData >= endOfData || |
michael@0 | 734 | static_cast<uint32_t>(endOfData) > *outDataLen) { |
michael@0 | 735 | return false; |
michael@0 | 736 | } |
michael@0 | 737 | |
michael@0 | 738 | // We want to return the buffer not offset by startOfData because it will be |
michael@0 | 739 | // parsed out later (probably by nsHTMLEditor::ParseCFHTML) when it is still |
michael@0 | 740 | // in CF_HTML format. |
michael@0 | 741 | *outDataLen = endOfData; |
michael@0 | 742 | return true; |
michael@0 | 743 | } |
michael@0 | 744 | |
michael@0 | 745 | |
michael@0 | 746 | // |
michael@0 | 747 | // FindUnicodeFromPlainText |
michael@0 | 748 | // |
michael@0 | 749 | // we are looking for text/unicode and we failed to find it on the clipboard first, |
michael@0 | 750 | // try again with text/plain. If that is present, convert it to unicode. |
michael@0 | 751 | // |
michael@0 | 752 | bool |
michael@0 | 753 | nsClipboard :: FindUnicodeFromPlainText ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen ) |
michael@0 | 754 | { |
michael@0 | 755 | bool dataFound = false; |
michael@0 | 756 | |
michael@0 | 757 | // we are looking for text/unicode and we failed to find it on the clipboard first, |
michael@0 | 758 | // try again with text/plain. If that is present, convert it to unicode. |
michael@0 | 759 | nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kTextMime), nullptr, outData, outDataLen); |
michael@0 | 760 | if ( NS_SUCCEEDED(loadResult) && *outData ) { |
michael@0 | 761 | const char* castedText = reinterpret_cast<char*>(*outData); |
michael@0 | 762 | char16_t* convertedText = nullptr; |
michael@0 | 763 | int32_t convertedTextLen = 0; |
michael@0 | 764 | nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode ( castedText, *outDataLen, |
michael@0 | 765 | &convertedText, &convertedTextLen ); |
michael@0 | 766 | if ( convertedText ) { |
michael@0 | 767 | // out with the old, in with the new |
michael@0 | 768 | nsMemory::Free(*outData); |
michael@0 | 769 | *outData = convertedText; |
michael@0 | 770 | *outDataLen = convertedTextLen * sizeof(char16_t); |
michael@0 | 771 | dataFound = true; |
michael@0 | 772 | } |
michael@0 | 773 | } // if plain text data on clipboard |
michael@0 | 774 | |
michael@0 | 775 | return dataFound; |
michael@0 | 776 | |
michael@0 | 777 | } // FindUnicodeFromPlainText |
michael@0 | 778 | |
michael@0 | 779 | |
michael@0 | 780 | // |
michael@0 | 781 | // FindURLFromLocalFile |
michael@0 | 782 | // |
michael@0 | 783 | // we are looking for a URL and couldn't find it, try again with looking for |
michael@0 | 784 | // a local file. If we have one, it may either be a normal file or an internet shortcut. |
michael@0 | 785 | // In both cases, however, we can get a URL (it will be a file:// url in the |
michael@0 | 786 | // local file case). |
michael@0 | 787 | // |
michael@0 | 788 | bool |
michael@0 | 789 | nsClipboard :: FindURLFromLocalFile ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen ) |
michael@0 | 790 | { |
michael@0 | 791 | bool dataFound = false; |
michael@0 | 792 | |
michael@0 | 793 | nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kFileMime), nullptr, outData, outDataLen); |
michael@0 | 794 | if ( NS_SUCCEEDED(loadResult) && *outData ) { |
michael@0 | 795 | // we have a file path in |data|. Is it an internet shortcut or a normal file? |
michael@0 | 796 | const nsDependentString filepath(static_cast<char16_t*>(*outData)); |
michael@0 | 797 | nsCOMPtr<nsIFile> file; |
michael@0 | 798 | nsresult rv = NS_NewLocalFile(filepath, true, getter_AddRefs(file)); |
michael@0 | 799 | if (NS_FAILED(rv)) { |
michael@0 | 800 | nsMemory::Free(*outData); |
michael@0 | 801 | return dataFound; |
michael@0 | 802 | } |
michael@0 | 803 | |
michael@0 | 804 | if ( IsInternetShortcut(filepath) ) { |
michael@0 | 805 | nsMemory::Free(*outData); |
michael@0 | 806 | nsAutoCString url; |
michael@0 | 807 | ResolveShortcut( file, url ); |
michael@0 | 808 | if ( !url.IsEmpty() ) { |
michael@0 | 809 | // convert it to unicode and pass it out |
michael@0 | 810 | nsDependentString urlString(UTF8ToNewUnicode(url)); |
michael@0 | 811 | // the internal mozilla URL format, text/x-moz-url, contains |
michael@0 | 812 | // URL\ntitle. We can guess the title from the file's name. |
michael@0 | 813 | nsAutoString title; |
michael@0 | 814 | file->GetLeafName(title); |
michael@0 | 815 | // We rely on IsInternetShortcut check that file has a .url extension. |
michael@0 | 816 | title.SetLength(title.Length() - 4); |
michael@0 | 817 | if (title.IsEmpty()) |
michael@0 | 818 | title = urlString; |
michael@0 | 819 | *outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + title); |
michael@0 | 820 | *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t); |
michael@0 | 821 | |
michael@0 | 822 | dataFound = true; |
michael@0 | 823 | } |
michael@0 | 824 | } |
michael@0 | 825 | else { |
michael@0 | 826 | // we have a normal file, use some Necko objects to get our file path |
michael@0 | 827 | nsAutoCString urlSpec; |
michael@0 | 828 | NS_GetURLSpecFromFile(file, urlSpec); |
michael@0 | 829 | |
michael@0 | 830 | // convert it to unicode and pass it out |
michael@0 | 831 | nsMemory::Free(*outData); |
michael@0 | 832 | *outData = UTF8ToNewUnicode(urlSpec); |
michael@0 | 833 | *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t); |
michael@0 | 834 | dataFound = true; |
michael@0 | 835 | } // else regular file |
michael@0 | 836 | } |
michael@0 | 837 | |
michael@0 | 838 | return dataFound; |
michael@0 | 839 | } // FindURLFromLocalFile |
michael@0 | 840 | |
michael@0 | 841 | // |
michael@0 | 842 | // FindURLFromNativeURL |
michael@0 | 843 | // |
michael@0 | 844 | // we are looking for a URL and couldn't find it using our internal |
michael@0 | 845 | // URL flavor, so look for it using the native URL flavor, |
michael@0 | 846 | // CF_INETURLSTRW (We don't handle CF_INETURLSTRA currently) |
michael@0 | 847 | // |
michael@0 | 848 | bool |
michael@0 | 849 | nsClipboard :: FindURLFromNativeURL ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen ) |
michael@0 | 850 | { |
michael@0 | 851 | bool dataFound = false; |
michael@0 | 852 | |
michael@0 | 853 | void* tempOutData = nullptr; |
michael@0 | 854 | uint32_t tempDataLen = 0; |
michael@0 | 855 | |
michael@0 | 856 | nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, ::RegisterClipboardFormat(CFSTR_INETURLW), nullptr, &tempOutData, &tempDataLen); |
michael@0 | 857 | if ( NS_SUCCEEDED(loadResult) && tempOutData ) { |
michael@0 | 858 | nsDependentString urlString(static_cast<char16_t*>(tempOutData)); |
michael@0 | 859 | // the internal mozilla URL format, text/x-moz-url, contains |
michael@0 | 860 | // URL\ntitle. Since we don't actually have a title here, |
michael@0 | 861 | // just repeat the URL to fake it. |
michael@0 | 862 | *outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + urlString); |
michael@0 | 863 | *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t); |
michael@0 | 864 | nsMemory::Free(tempOutData); |
michael@0 | 865 | dataFound = true; |
michael@0 | 866 | } |
michael@0 | 867 | else { |
michael@0 | 868 | loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, ::RegisterClipboardFormat(CFSTR_INETURLA), nullptr, &tempOutData, &tempDataLen); |
michael@0 | 869 | if ( NS_SUCCEEDED(loadResult) && tempOutData ) { |
michael@0 | 870 | // CFSTR_INETURLA is (currently) equal to CFSTR_SHELLURL which is equal to CF_TEXT |
michael@0 | 871 | // which is by definition ANSI encoded. |
michael@0 | 872 | nsCString urlUnescapedA; |
michael@0 | 873 | bool unescaped = NS_UnescapeURL(static_cast<char*>(tempOutData), tempDataLen, esc_OnlyNonASCII | esc_SkipControl, urlUnescapedA); |
michael@0 | 874 | |
michael@0 | 875 | nsString urlString; |
michael@0 | 876 | if (unescaped) |
michael@0 | 877 | NS_CopyNativeToUnicode(urlUnescapedA, urlString); |
michael@0 | 878 | else |
michael@0 | 879 | NS_CopyNativeToUnicode(nsDependentCString(static_cast<char*>(tempOutData), tempDataLen), urlString); |
michael@0 | 880 | |
michael@0 | 881 | // the internal mozilla URL format, text/x-moz-url, contains |
michael@0 | 882 | // URL\ntitle. Since we don't actually have a title here, |
michael@0 | 883 | // just repeat the URL to fake it. |
michael@0 | 884 | *outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + urlString); |
michael@0 | 885 | *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t); |
michael@0 | 886 | nsMemory::Free(tempOutData); |
michael@0 | 887 | dataFound = true; |
michael@0 | 888 | } |
michael@0 | 889 | } |
michael@0 | 890 | |
michael@0 | 891 | return dataFound; |
michael@0 | 892 | } // FindURLFromNativeURL |
michael@0 | 893 | |
michael@0 | 894 | // |
michael@0 | 895 | // ResolveShortcut |
michael@0 | 896 | // |
michael@0 | 897 | void |
michael@0 | 898 | nsClipboard :: ResolveShortcut ( nsIFile* aFile, nsACString& outURL ) |
michael@0 | 899 | { |
michael@0 | 900 | nsCOMPtr<nsIFileProtocolHandler> fph; |
michael@0 | 901 | nsresult rv = NS_GetFileProtocolHandler(getter_AddRefs(fph)); |
michael@0 | 902 | if (NS_FAILED(rv)) |
michael@0 | 903 | return; |
michael@0 | 904 | |
michael@0 | 905 | nsCOMPtr<nsIURI> uri; |
michael@0 | 906 | rv = fph->ReadURLFile(aFile, getter_AddRefs(uri)); |
michael@0 | 907 | if (NS_FAILED(rv)) |
michael@0 | 908 | return; |
michael@0 | 909 | |
michael@0 | 910 | uri->GetSpec(outURL); |
michael@0 | 911 | } // ResolveShortcut |
michael@0 | 912 | |
michael@0 | 913 | |
michael@0 | 914 | // |
michael@0 | 915 | // IsInternetShortcut |
michael@0 | 916 | // |
michael@0 | 917 | // A file is an Internet Shortcut if it ends with .URL |
michael@0 | 918 | // |
michael@0 | 919 | bool |
michael@0 | 920 | nsClipboard :: IsInternetShortcut ( const nsAString& inFileName ) |
michael@0 | 921 | { |
michael@0 | 922 | return StringEndsWith(inFileName, NS_LITERAL_STRING(".url"), nsCaseInsensitiveStringComparator()); |
michael@0 | 923 | } // IsInternetShortcut |
michael@0 | 924 | |
michael@0 | 925 | |
michael@0 | 926 | //------------------------------------------------------------------------- |
michael@0 | 927 | NS_IMETHODIMP |
michael@0 | 928 | nsClipboard::GetNativeClipboardData ( nsITransferable * aTransferable, int32_t aWhichClipboard ) |
michael@0 | 929 | { |
michael@0 | 930 | // make sure we have a good transferable |
michael@0 | 931 | if ( !aTransferable || aWhichClipboard != kGlobalClipboard ) |
michael@0 | 932 | return NS_ERROR_FAILURE; |
michael@0 | 933 | |
michael@0 | 934 | nsresult res; |
michael@0 | 935 | |
michael@0 | 936 | // This makes sure we can use the OLE functionality for the clipboard |
michael@0 | 937 | IDataObject * dataObj; |
michael@0 | 938 | if (S_OK == ::OleGetClipboard(&dataObj)) { |
michael@0 | 939 | // Use OLE IDataObject for clipboard operations |
michael@0 | 940 | res = GetDataFromDataObject(dataObj, 0, nullptr, aTransferable); |
michael@0 | 941 | dataObj->Release(); |
michael@0 | 942 | } |
michael@0 | 943 | else { |
michael@0 | 944 | // do it the old manual way |
michael@0 | 945 | res = GetDataFromDataObject(nullptr, 0, mWindow, aTransferable); |
michael@0 | 946 | } |
michael@0 | 947 | return res; |
michael@0 | 948 | |
michael@0 | 949 | } |
michael@0 | 950 | |
michael@0 | 951 | NS_IMETHODIMP |
michael@0 | 952 | nsClipboard::EmptyClipboard(int32_t aWhichClipboard) |
michael@0 | 953 | { |
michael@0 | 954 | // Some programs such as ZoneAlarm monitor clipboard usage and then open the |
michael@0 | 955 | // clipboard to scan it. If we i) empty and then ii) set data, then the |
michael@0 | 956 | // 'set data' can sometimes fail with access denied becacuse another program |
michael@0 | 957 | // has the clipboard open. So to avoid this race condition for OpenClipboard |
michael@0 | 958 | // we do not empty the clipboard when we're setting it. |
michael@0 | 959 | if (aWhichClipboard == kGlobalClipboard && !mEmptyingForSetData) { |
michael@0 | 960 | OleSetClipboard(nullptr); |
michael@0 | 961 | } |
michael@0 | 962 | return nsBaseClipboard::EmptyClipboard(aWhichClipboard); |
michael@0 | 963 | } |
michael@0 | 964 | |
michael@0 | 965 | //------------------------------------------------------------------------- |
michael@0 | 966 | NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, |
michael@0 | 967 | uint32_t aLength, |
michael@0 | 968 | int32_t aWhichClipboard, |
michael@0 | 969 | bool *_retval) |
michael@0 | 970 | { |
michael@0 | 971 | *_retval = false; |
michael@0 | 972 | if (aWhichClipboard != kGlobalClipboard || !aFlavorList) |
michael@0 | 973 | return NS_OK; |
michael@0 | 974 | |
michael@0 | 975 | for (uint32_t i = 0;i < aLength; ++i) { |
michael@0 | 976 | #ifdef DEBUG |
michael@0 | 977 | if (strcmp(aFlavorList[i], kTextMime) == 0) |
michael@0 | 978 | NS_WARNING ( "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD" ); |
michael@0 | 979 | #endif |
michael@0 | 980 | |
michael@0 | 981 | UINT format = GetFormat(aFlavorList[i]); |
michael@0 | 982 | if (IsClipboardFormatAvailable(format)) { |
michael@0 | 983 | *_retval = true; |
michael@0 | 984 | break; |
michael@0 | 985 | } |
michael@0 | 986 | else { |
michael@0 | 987 | // We haven't found the exact flavor the client asked for, but maybe we can |
michael@0 | 988 | // still find it from something else that's on the clipboard... |
michael@0 | 989 | if (strcmp(aFlavorList[i], kUnicodeMime) == 0) { |
michael@0 | 990 | // client asked for unicode and it wasn't present, check if we have CF_TEXT. |
michael@0 | 991 | // We'll handle the actual data substitution in the data object. |
michael@0 | 992 | if (IsClipboardFormatAvailable(GetFormat(kTextMime))) |
michael@0 | 993 | *_retval = true; |
michael@0 | 994 | } |
michael@0 | 995 | } |
michael@0 | 996 | } |
michael@0 | 997 | |
michael@0 | 998 | return NS_OK; |
michael@0 | 999 | } |