Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "nsReadableUtils.h" |
michael@0 | 7 | |
michael@0 | 8 | // Local Includes |
michael@0 | 9 | #include "nsContentAreaDragDrop.h" |
michael@0 | 10 | |
michael@0 | 11 | // Helper Classes |
michael@0 | 12 | #include "nsString.h" |
michael@0 | 13 | |
michael@0 | 14 | // Interfaces needed to be included |
michael@0 | 15 | #include "nsCopySupport.h" |
michael@0 | 16 | #include "nsIDOMUIEvent.h" |
michael@0 | 17 | #include "nsISelection.h" |
michael@0 | 18 | #include "nsISelectionController.h" |
michael@0 | 19 | #include "nsIDOMNode.h" |
michael@0 | 20 | #include "nsIDOMNodeList.h" |
michael@0 | 21 | #include "nsIDOMEvent.h" |
michael@0 | 22 | #include "nsIDOMDragEvent.h" |
michael@0 | 23 | #include "nsPIDOMWindow.h" |
michael@0 | 24 | #include "nsIDOMRange.h" |
michael@0 | 25 | #include "nsIFormControl.h" |
michael@0 | 26 | #include "nsIDOMHTMLAreaElement.h" |
michael@0 | 27 | #include "nsIDOMHTMLAnchorElement.h" |
michael@0 | 28 | #include "nsITransferable.h" |
michael@0 | 29 | #include "nsComponentManagerUtils.h" |
michael@0 | 30 | #include "nsXPCOM.h" |
michael@0 | 31 | #include "nsISupportsPrimitives.h" |
michael@0 | 32 | #include "nsServiceManagerUtils.h" |
michael@0 | 33 | #include "nsNetUtil.h" |
michael@0 | 34 | #include "nsIFile.h" |
michael@0 | 35 | #include "nsIWebNavigation.h" |
michael@0 | 36 | #include "nsIDocShell.h" |
michael@0 | 37 | #include "nsIContent.h" |
michael@0 | 38 | #include "nsIImageLoadingContent.h" |
michael@0 | 39 | #include "nsITextControlElement.h" |
michael@0 | 40 | #include "nsUnicharUtils.h" |
michael@0 | 41 | #include "nsIURL.h" |
michael@0 | 42 | #include "nsIDocument.h" |
michael@0 | 43 | #include "nsIScriptSecurityManager.h" |
michael@0 | 44 | #include "nsIPrincipal.h" |
michael@0 | 45 | #include "nsIDocShellTreeItem.h" |
michael@0 | 46 | #include "nsIWebBrowserPersist.h" |
michael@0 | 47 | #include "nsEscape.h" |
michael@0 | 48 | #include "nsContentUtils.h" |
michael@0 | 49 | #include "nsIMIMEService.h" |
michael@0 | 50 | #include "imgIContainer.h" |
michael@0 | 51 | #include "imgIRequest.h" |
michael@0 | 52 | #include "mozilla/dom/DataTransfer.h" |
michael@0 | 53 | #include "nsIMIMEInfo.h" |
michael@0 | 54 | #include "nsRange.h" |
michael@0 | 55 | #include "mozilla/dom/Element.h" |
michael@0 | 56 | #include "mozilla/dom/HTMLAreaElement.h" |
michael@0 | 57 | |
michael@0 | 58 | using namespace mozilla::dom; |
michael@0 | 59 | |
michael@0 | 60 | class MOZ_STACK_CLASS DragDataProducer |
michael@0 | 61 | { |
michael@0 | 62 | public: |
michael@0 | 63 | DragDataProducer(nsPIDOMWindow* aWindow, |
michael@0 | 64 | nsIContent* aTarget, |
michael@0 | 65 | nsIContent* aSelectionTargetNode, |
michael@0 | 66 | bool aIsAltKeyPressed); |
michael@0 | 67 | nsresult Produce(DataTransfer* aDataTransfer, |
michael@0 | 68 | bool* aCanDrag, |
michael@0 | 69 | nsISelection** aSelection, |
michael@0 | 70 | nsIContent** aDragNode); |
michael@0 | 71 | |
michael@0 | 72 | private: |
michael@0 | 73 | void AddString(DataTransfer* aDataTransfer, |
michael@0 | 74 | const nsAString& aFlavor, |
michael@0 | 75 | const nsAString& aData, |
michael@0 | 76 | nsIPrincipal* aPrincipal); |
michael@0 | 77 | nsresult AddStringsToDataTransfer(nsIContent* aDragNode, |
michael@0 | 78 | DataTransfer* aDataTransfer); |
michael@0 | 79 | static nsresult GetDraggableSelectionData(nsISelection* inSelection, |
michael@0 | 80 | nsIContent* inRealTargetNode, |
michael@0 | 81 | nsIContent **outImageOrLinkNode, |
michael@0 | 82 | bool* outDragSelectedText); |
michael@0 | 83 | static already_AddRefed<nsIContent> FindParentLinkNode(nsIContent* inNode); |
michael@0 | 84 | static void GetAnchorURL(nsIContent* inNode, nsAString& outURL); |
michael@0 | 85 | static void GetNodeString(nsIContent* inNode, nsAString & outNodeString); |
michael@0 | 86 | static void CreateLinkText(const nsAString& inURL, const nsAString & inText, |
michael@0 | 87 | nsAString& outLinkText); |
michael@0 | 88 | static void GetSelectedLink(nsISelection* inSelection, |
michael@0 | 89 | nsIContent **outLinkNode); |
michael@0 | 90 | |
michael@0 | 91 | nsCOMPtr<nsPIDOMWindow> mWindow; |
michael@0 | 92 | nsCOMPtr<nsIContent> mTarget; |
michael@0 | 93 | nsCOMPtr<nsIContent> mSelectionTargetNode; |
michael@0 | 94 | bool mIsAltKeyPressed; |
michael@0 | 95 | |
michael@0 | 96 | nsString mUrlString; |
michael@0 | 97 | nsString mImageSourceString; |
michael@0 | 98 | nsString mImageDestFileName; |
michael@0 | 99 | nsString mTitleString; |
michael@0 | 100 | // will be filled automatically if you fill urlstring |
michael@0 | 101 | nsString mHtmlString; |
michael@0 | 102 | nsString mContextString; |
michael@0 | 103 | nsString mInfoString; |
michael@0 | 104 | |
michael@0 | 105 | bool mIsAnchor; |
michael@0 | 106 | nsCOMPtr<imgIContainer> mImage; |
michael@0 | 107 | }; |
michael@0 | 108 | |
michael@0 | 109 | |
michael@0 | 110 | nsresult |
michael@0 | 111 | nsContentAreaDragDrop::GetDragData(nsPIDOMWindow* aWindow, |
michael@0 | 112 | nsIContent* aTarget, |
michael@0 | 113 | nsIContent* aSelectionTargetNode, |
michael@0 | 114 | bool aIsAltKeyPressed, |
michael@0 | 115 | DataTransfer* aDataTransfer, |
michael@0 | 116 | bool* aCanDrag, |
michael@0 | 117 | nsISelection** aSelection, |
michael@0 | 118 | nsIContent** aDragNode) |
michael@0 | 119 | { |
michael@0 | 120 | NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG); |
michael@0 | 121 | |
michael@0 | 122 | *aCanDrag = true; |
michael@0 | 123 | |
michael@0 | 124 | DragDataProducer |
michael@0 | 125 | provider(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed); |
michael@0 | 126 | return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode); |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | |
michael@0 | 130 | NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider) |
michael@0 | 131 | |
michael@0 | 132 | // SaveURIToFile |
michael@0 | 133 | // used on platforms where it's possible to drag items (e.g. images) |
michael@0 | 134 | // into the file system |
michael@0 | 135 | nsresult |
michael@0 | 136 | nsContentAreaDragDropDataProvider::SaveURIToFile(nsAString& inSourceURIString, |
michael@0 | 137 | nsIFile* inDestFile, |
michael@0 | 138 | bool isPrivate) |
michael@0 | 139 | { |
michael@0 | 140 | nsCOMPtr<nsIURI> sourceURI; |
michael@0 | 141 | nsresult rv = NS_NewURI(getter_AddRefs(sourceURI), inSourceURIString); |
michael@0 | 142 | if (NS_FAILED(rv)) { |
michael@0 | 143 | return NS_ERROR_FAILURE; |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI); |
michael@0 | 147 | if (!sourceURL) { |
michael@0 | 148 | return NS_ERROR_NO_INTERFACE; |
michael@0 | 149 | } |
michael@0 | 150 | |
michael@0 | 151 | rv = inDestFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); |
michael@0 | 152 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 153 | |
michael@0 | 154 | // we rely on the fact that the WPB is refcounted by the channel etc, |
michael@0 | 155 | // so we don't keep a ref to it. It will die when finished. |
michael@0 | 156 | nsCOMPtr<nsIWebBrowserPersist> persist = |
michael@0 | 157 | do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", |
michael@0 | 158 | &rv); |
michael@0 | 159 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 160 | |
michael@0 | 161 | persist->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION); |
michael@0 | 162 | |
michael@0 | 163 | return persist->SavePrivacyAwareURI(sourceURI, nullptr, nullptr, nullptr, nullptr, |
michael@0 | 164 | inDestFile, isPrivate); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | // This is our nsIFlavorDataProvider callback. There are several |
michael@0 | 168 | // assumptions here that make this work: |
michael@0 | 169 | // |
michael@0 | 170 | // 1. Someone put a kFilePromiseURLMime flavor into the transferable |
michael@0 | 171 | // with the source URI of the file to save (as a string). We did |
michael@0 | 172 | // that in AddStringsToDataTransfer. |
michael@0 | 173 | // |
michael@0 | 174 | // 2. Someone put a kFilePromiseDirectoryMime flavor into the |
michael@0 | 175 | // transferable with an nsIFile for the directory we are to |
michael@0 | 176 | // save in. That has to be done by platform-specific code (in |
michael@0 | 177 | // widget), which gets the destination directory from |
michael@0 | 178 | // OS-specific drag information. |
michael@0 | 179 | // |
michael@0 | 180 | NS_IMETHODIMP |
michael@0 | 181 | nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable, |
michael@0 | 182 | const char *aFlavor, |
michael@0 | 183 | nsISupports **aData, |
michael@0 | 184 | uint32_t *aDataLen) |
michael@0 | 185 | { |
michael@0 | 186 | NS_ENSURE_ARG_POINTER(aData && aDataLen); |
michael@0 | 187 | *aData = nullptr; |
michael@0 | 188 | *aDataLen = 0; |
michael@0 | 189 | |
michael@0 | 190 | nsresult rv = NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 191 | |
michael@0 | 192 | if (strcmp(aFlavor, kFilePromiseMime) == 0) { |
michael@0 | 193 | // get the URI from the kFilePromiseURLMime flavor |
michael@0 | 194 | NS_ENSURE_ARG(aTransferable); |
michael@0 | 195 | nsCOMPtr<nsISupports> tmp; |
michael@0 | 196 | uint32_t dataSize = 0; |
michael@0 | 197 | aTransferable->GetTransferData(kFilePromiseURLMime, |
michael@0 | 198 | getter_AddRefs(tmp), &dataSize); |
michael@0 | 199 | nsCOMPtr<nsISupportsString> supportsString = |
michael@0 | 200 | do_QueryInterface(tmp); |
michael@0 | 201 | if (!supportsString) |
michael@0 | 202 | return NS_ERROR_FAILURE; |
michael@0 | 203 | |
michael@0 | 204 | nsAutoString sourceURLString; |
michael@0 | 205 | supportsString->GetData(sourceURLString); |
michael@0 | 206 | if (sourceURLString.IsEmpty()) |
michael@0 | 207 | return NS_ERROR_FAILURE; |
michael@0 | 208 | |
michael@0 | 209 | aTransferable->GetTransferData(kFilePromiseDestFilename, |
michael@0 | 210 | getter_AddRefs(tmp), &dataSize); |
michael@0 | 211 | supportsString = do_QueryInterface(tmp); |
michael@0 | 212 | if (!supportsString) |
michael@0 | 213 | return NS_ERROR_FAILURE; |
michael@0 | 214 | |
michael@0 | 215 | nsAutoString targetFilename; |
michael@0 | 216 | supportsString->GetData(targetFilename); |
michael@0 | 217 | if (targetFilename.IsEmpty()) |
michael@0 | 218 | return NS_ERROR_FAILURE; |
michael@0 | 219 | |
michael@0 | 220 | // get the target directory from the kFilePromiseDirectoryMime |
michael@0 | 221 | // flavor |
michael@0 | 222 | nsCOMPtr<nsISupports> dirPrimitive; |
michael@0 | 223 | dataSize = 0; |
michael@0 | 224 | aTransferable->GetTransferData(kFilePromiseDirectoryMime, |
michael@0 | 225 | getter_AddRefs(dirPrimitive), &dataSize); |
michael@0 | 226 | nsCOMPtr<nsIFile> destDirectory = do_QueryInterface(dirPrimitive); |
michael@0 | 227 | if (!destDirectory) |
michael@0 | 228 | return NS_ERROR_FAILURE; |
michael@0 | 229 | |
michael@0 | 230 | nsCOMPtr<nsIFile> file; |
michael@0 | 231 | rv = destDirectory->Clone(getter_AddRefs(file)); |
michael@0 | 232 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 233 | |
michael@0 | 234 | file->Append(targetFilename); |
michael@0 | 235 | |
michael@0 | 236 | bool isPrivate; |
michael@0 | 237 | aTransferable->GetIsPrivateData(&isPrivate); |
michael@0 | 238 | |
michael@0 | 239 | rv = SaveURIToFile(sourceURLString, file, isPrivate); |
michael@0 | 240 | // send back an nsIFile |
michael@0 | 241 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 242 | CallQueryInterface(file, aData); |
michael@0 | 243 | *aDataLen = sizeof(nsIFile*); |
michael@0 | 244 | } |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | return rv; |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | DragDataProducer::DragDataProducer(nsPIDOMWindow* aWindow, |
michael@0 | 251 | nsIContent* aTarget, |
michael@0 | 252 | nsIContent* aSelectionTargetNode, |
michael@0 | 253 | bool aIsAltKeyPressed) |
michael@0 | 254 | : mWindow(aWindow), |
michael@0 | 255 | mTarget(aTarget), |
michael@0 | 256 | mSelectionTargetNode(aSelectionTargetNode), |
michael@0 | 257 | mIsAltKeyPressed(aIsAltKeyPressed), |
michael@0 | 258 | mIsAnchor(false) |
michael@0 | 259 | { |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | |
michael@0 | 263 | // |
michael@0 | 264 | // FindParentLinkNode |
michael@0 | 265 | // |
michael@0 | 266 | // Finds the parent with the given link tag starting at |inNode|. If |
michael@0 | 267 | // it gets up to the root without finding it, we stop looking and |
michael@0 | 268 | // return null. |
michael@0 | 269 | // |
michael@0 | 270 | already_AddRefed<nsIContent> |
michael@0 | 271 | DragDataProducer::FindParentLinkNode(nsIContent* inNode) |
michael@0 | 272 | { |
michael@0 | 273 | nsIContent* content = inNode; |
michael@0 | 274 | if (!content) { |
michael@0 | 275 | // That must have been the document node; nothing else to do here; |
michael@0 | 276 | return nullptr; |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | for (; content; content = content->GetParent()) { |
michael@0 | 280 | if (nsContentUtils::IsDraggableLink(content)) { |
michael@0 | 281 | nsCOMPtr<nsIContent> ret = content; |
michael@0 | 282 | return ret.forget(); |
michael@0 | 283 | } |
michael@0 | 284 | } |
michael@0 | 285 | |
michael@0 | 286 | return nullptr; |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | |
michael@0 | 290 | // |
michael@0 | 291 | // GetAnchorURL |
michael@0 | 292 | // |
michael@0 | 293 | void |
michael@0 | 294 | DragDataProducer::GetAnchorURL(nsIContent* inNode, nsAString& outURL) |
michael@0 | 295 | { |
michael@0 | 296 | nsCOMPtr<nsIURI> linkURI; |
michael@0 | 297 | if (!inNode || !inNode->IsLink(getter_AddRefs(linkURI))) { |
michael@0 | 298 | // Not a link |
michael@0 | 299 | outURL.Truncate(); |
michael@0 | 300 | return; |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | nsAutoCString spec; |
michael@0 | 304 | linkURI->GetSpec(spec); |
michael@0 | 305 | CopyUTF8toUTF16(spec, outURL); |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | |
michael@0 | 309 | // |
michael@0 | 310 | // CreateLinkText |
michael@0 | 311 | // |
michael@0 | 312 | // Creates the html for an anchor in the form |
michael@0 | 313 | // <a href="inURL">inText</a> |
michael@0 | 314 | // |
michael@0 | 315 | void |
michael@0 | 316 | DragDataProducer::CreateLinkText(const nsAString& inURL, |
michael@0 | 317 | const nsAString & inText, |
michael@0 | 318 | nsAString& outLinkText) |
michael@0 | 319 | { |
michael@0 | 320 | // use a temp var in case |inText| is the same string as |
michael@0 | 321 | // |outLinkText| to avoid overwriting it while building up the |
michael@0 | 322 | // string in pieces. |
michael@0 | 323 | nsAutoString linkText(NS_LITERAL_STRING("<a href=\"") + |
michael@0 | 324 | inURL + |
michael@0 | 325 | NS_LITERAL_STRING("\">") + |
michael@0 | 326 | inText + |
michael@0 | 327 | NS_LITERAL_STRING("</a>") ); |
michael@0 | 328 | |
michael@0 | 329 | outLinkText = linkText; |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | |
michael@0 | 333 | // |
michael@0 | 334 | // GetNodeString |
michael@0 | 335 | // |
michael@0 | 336 | // Gets the text associated with a node |
michael@0 | 337 | // |
michael@0 | 338 | void |
michael@0 | 339 | DragDataProducer::GetNodeString(nsIContent* inNode, |
michael@0 | 340 | nsAString & outNodeString) |
michael@0 | 341 | { |
michael@0 | 342 | nsCOMPtr<nsINode> node = inNode; |
michael@0 | 343 | |
michael@0 | 344 | outNodeString.Truncate(); |
michael@0 | 345 | |
michael@0 | 346 | // use a range to get the text-equivalent of the node |
michael@0 | 347 | nsCOMPtr<nsIDocument> doc = node->OwnerDoc(); |
michael@0 | 348 | mozilla::ErrorResult rv; |
michael@0 | 349 | nsRefPtr<nsRange> range = doc->CreateRange(rv); |
michael@0 | 350 | if (range) { |
michael@0 | 351 | range->SelectNode(*node, rv); |
michael@0 | 352 | range->ToString(outNodeString); |
michael@0 | 353 | } |
michael@0 | 354 | } |
michael@0 | 355 | |
michael@0 | 356 | nsresult |
michael@0 | 357 | DragDataProducer::Produce(DataTransfer* aDataTransfer, |
michael@0 | 358 | bool* aCanDrag, |
michael@0 | 359 | nsISelection** aSelection, |
michael@0 | 360 | nsIContent** aDragNode) |
michael@0 | 361 | { |
michael@0 | 362 | NS_PRECONDITION(aCanDrag && aSelection && aDataTransfer && aDragNode, |
michael@0 | 363 | "null pointer passed to Produce"); |
michael@0 | 364 | NS_ASSERTION(mWindow, "window not set"); |
michael@0 | 365 | NS_ASSERTION(mSelectionTargetNode, "selection target node should have been set"); |
michael@0 | 366 | |
michael@0 | 367 | *aDragNode = nullptr; |
michael@0 | 368 | |
michael@0 | 369 | nsresult rv; |
michael@0 | 370 | nsIContent* dragNode = nullptr; |
michael@0 | 371 | *aSelection = nullptr; |
michael@0 | 372 | |
michael@0 | 373 | // Find the selection to see what we could be dragging and if what we're |
michael@0 | 374 | // dragging is in what is selected. If this is an editable textbox, use |
michael@0 | 375 | // the textbox's selection, otherwise use the window's selection. |
michael@0 | 376 | nsCOMPtr<nsISelection> selection; |
michael@0 | 377 | nsIContent* editingElement = mSelectionTargetNode->IsEditable() ? |
michael@0 | 378 | mSelectionTargetNode->GetEditingHost() : nullptr; |
michael@0 | 379 | nsCOMPtr<nsITextControlElement> textControl = |
michael@0 | 380 | nsITextControlElement::GetTextControlElementFromEditingHost(editingElement); |
michael@0 | 381 | if (textControl) { |
michael@0 | 382 | nsISelectionController* selcon = textControl->GetSelectionController(); |
michael@0 | 383 | if (selcon) { |
michael@0 | 384 | selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); |
michael@0 | 385 | } |
michael@0 | 386 | |
michael@0 | 387 | if (!selection) |
michael@0 | 388 | return NS_OK; |
michael@0 | 389 | } |
michael@0 | 390 | else { |
michael@0 | 391 | mWindow->GetSelection(getter_AddRefs(selection)); |
michael@0 | 392 | if (!selection) |
michael@0 | 393 | return NS_OK; |
michael@0 | 394 | |
michael@0 | 395 | // Check if the node is inside a form control. Don't set aCanDrag to false |
michael@0 | 396 | //however, as we still want to allow the drag. |
michael@0 | 397 | nsCOMPtr<nsIContent> findFormNode = mSelectionTargetNode; |
michael@0 | 398 | nsIContent* findFormParent = findFormNode->GetParent(); |
michael@0 | 399 | while (findFormParent) { |
michael@0 | 400 | nsCOMPtr<nsIFormControl> form(do_QueryInterface(findFormParent)); |
michael@0 | 401 | if (form && !form->AllowDraggableChildren()) { |
michael@0 | 402 | return NS_OK; |
michael@0 | 403 | } |
michael@0 | 404 | findFormParent = findFormParent->GetParent(); |
michael@0 | 405 | } |
michael@0 | 406 | } |
michael@0 | 407 | |
michael@0 | 408 | // if set, serialize the content under this node |
michael@0 | 409 | nsCOMPtr<nsIContent> nodeToSerialize; |
michael@0 | 410 | |
michael@0 | 411 | nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(mWindow); |
michael@0 | 412 | nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav); |
michael@0 | 413 | const bool isChromeShell = |
michael@0 | 414 | dsti && dsti->ItemType() == nsIDocShellTreeItem::typeChrome; |
michael@0 | 415 | |
michael@0 | 416 | // In chrome shells, only allow dragging inside editable areas. |
michael@0 | 417 | if (isChromeShell && !editingElement) |
michael@0 | 418 | return NS_OK; |
michael@0 | 419 | |
michael@0 | 420 | if (isChromeShell && textControl) { |
michael@0 | 421 | // Only use the selection if the target node is in the selection. |
michael@0 | 422 | bool selectionContainsTarget = false; |
michael@0 | 423 | nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(mSelectionTargetNode); |
michael@0 | 424 | selection->ContainsNode(targetNode, false, &selectionContainsTarget); |
michael@0 | 425 | if (!selectionContainsTarget) |
michael@0 | 426 | return NS_OK; |
michael@0 | 427 | |
michael@0 | 428 | selection.swap(*aSelection); |
michael@0 | 429 | } |
michael@0 | 430 | else { |
michael@0 | 431 | // In content shells, a number of checks are made below to determine |
michael@0 | 432 | // whether an image or a link is being dragged. If so, add additional |
michael@0 | 433 | // data to the data transfer. This is also done for chrome shells, but |
michael@0 | 434 | // only when in a non-textbox editor. |
michael@0 | 435 | |
michael@0 | 436 | bool haveSelectedContent = false; |
michael@0 | 437 | |
michael@0 | 438 | // possible parent link node |
michael@0 | 439 | nsCOMPtr<nsIContent> parentLink; |
michael@0 | 440 | nsCOMPtr<nsIContent> draggedNode; |
michael@0 | 441 | |
michael@0 | 442 | { |
michael@0 | 443 | // only drag form elements by using the alt key, |
michael@0 | 444 | // otherwise buttons and select widgets are hard to use |
michael@0 | 445 | |
michael@0 | 446 | // Note that while <object> elements implement nsIFormControl, we should |
michael@0 | 447 | // really allow dragging them if they happen to be images. |
michael@0 | 448 | nsCOMPtr<nsIFormControl> form(do_QueryInterface(mTarget)); |
michael@0 | 449 | if (form && !mIsAltKeyPressed && form->GetType() != NS_FORM_OBJECT) { |
michael@0 | 450 | *aCanDrag = false; |
michael@0 | 451 | return NS_OK; |
michael@0 | 452 | } |
michael@0 | 453 | |
michael@0 | 454 | draggedNode = mTarget; |
michael@0 | 455 | } |
michael@0 | 456 | |
michael@0 | 457 | nsCOMPtr<nsIDOMHTMLAreaElement> area; // client-side image map |
michael@0 | 458 | nsCOMPtr<nsIImageLoadingContent> image; |
michael@0 | 459 | nsCOMPtr<nsIDOMHTMLAnchorElement> link; |
michael@0 | 460 | |
michael@0 | 461 | nsCOMPtr<nsIContent> selectedImageOrLinkNode; |
michael@0 | 462 | GetDraggableSelectionData(selection, mSelectionTargetNode, |
michael@0 | 463 | getter_AddRefs(selectedImageOrLinkNode), |
michael@0 | 464 | &haveSelectedContent); |
michael@0 | 465 | |
michael@0 | 466 | // either plain text or anchor text is selected |
michael@0 | 467 | if (haveSelectedContent) { |
michael@0 | 468 | link = do_QueryInterface(selectedImageOrLinkNode); |
michael@0 | 469 | if (link && mIsAltKeyPressed) { |
michael@0 | 470 | // if alt is pressed, select the link text instead of drag the link |
michael@0 | 471 | *aCanDrag = false; |
michael@0 | 472 | return NS_OK; |
michael@0 | 473 | } |
michael@0 | 474 | |
michael@0 | 475 | selection.swap(*aSelection); |
michael@0 | 476 | } else if (selectedImageOrLinkNode) { |
michael@0 | 477 | // an image is selected |
michael@0 | 478 | image = do_QueryInterface(selectedImageOrLinkNode); |
michael@0 | 479 | } else { |
michael@0 | 480 | // nothing is selected - |
michael@0 | 481 | // |
michael@0 | 482 | // look for draggable elements under the mouse |
michael@0 | 483 | // |
michael@0 | 484 | // if the alt key is down, don't start a drag if we're in an |
michael@0 | 485 | // anchor because we want to do selection. |
michael@0 | 486 | parentLink = FindParentLinkNode(draggedNode); |
michael@0 | 487 | if (parentLink && mIsAltKeyPressed) { |
michael@0 | 488 | *aCanDrag = false; |
michael@0 | 489 | return NS_OK; |
michael@0 | 490 | } |
michael@0 | 491 | |
michael@0 | 492 | area = do_QueryInterface(draggedNode); |
michael@0 | 493 | image = do_QueryInterface(draggedNode); |
michael@0 | 494 | link = do_QueryInterface(draggedNode); |
michael@0 | 495 | } |
michael@0 | 496 | |
michael@0 | 497 | { |
michael@0 | 498 | // set for linked images, and links |
michael@0 | 499 | nsCOMPtr<nsIContent> linkNode; |
michael@0 | 500 | |
michael@0 | 501 | if (area) { |
michael@0 | 502 | // use the alt text (or, if missing, the href) as the title |
michael@0 | 503 | HTMLAreaElement* areaElem = static_cast<HTMLAreaElement*>(area.get()); |
michael@0 | 504 | areaElem->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString); |
michael@0 | 505 | if (mTitleString.IsEmpty()) { |
michael@0 | 506 | // this can be a relative link |
michael@0 | 507 | areaElem->GetAttribute(NS_LITERAL_STRING("href"), mTitleString); |
michael@0 | 508 | } |
michael@0 | 509 | |
michael@0 | 510 | // we'll generate HTML like <a href="absurl">alt text</a> |
michael@0 | 511 | mIsAnchor = true; |
michael@0 | 512 | |
michael@0 | 513 | // gives an absolute link |
michael@0 | 514 | GetAnchorURL(draggedNode, mUrlString); |
michael@0 | 515 | |
michael@0 | 516 | mHtmlString.AssignLiteral("<a href=\""); |
michael@0 | 517 | mHtmlString.Append(mUrlString); |
michael@0 | 518 | mHtmlString.AppendLiteral("\">"); |
michael@0 | 519 | mHtmlString.Append(mTitleString); |
michael@0 | 520 | mHtmlString.AppendLiteral("</a>"); |
michael@0 | 521 | |
michael@0 | 522 | dragNode = draggedNode; |
michael@0 | 523 | } else if (image) { |
michael@0 | 524 | mIsAnchor = true; |
michael@0 | 525 | // grab the href as the url, use alt text as the title of the |
michael@0 | 526 | // area if it's there. the drag data is the image tag and src |
michael@0 | 527 | // attribute. |
michael@0 | 528 | nsCOMPtr<nsIURI> imageURI; |
michael@0 | 529 | image->GetCurrentURI(getter_AddRefs(imageURI)); |
michael@0 | 530 | if (imageURI) { |
michael@0 | 531 | nsAutoCString spec; |
michael@0 | 532 | imageURI->GetSpec(spec); |
michael@0 | 533 | CopyUTF8toUTF16(spec, mUrlString); |
michael@0 | 534 | } |
michael@0 | 535 | |
michael@0 | 536 | nsCOMPtr<nsIDOMElement> imageElement(do_QueryInterface(image)); |
michael@0 | 537 | // XXXbz Shouldn't we use the "title" attr for title? Using |
michael@0 | 538 | // "alt" seems very wrong.... |
michael@0 | 539 | if (imageElement) { |
michael@0 | 540 | imageElement->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString); |
michael@0 | 541 | } |
michael@0 | 542 | |
michael@0 | 543 | if (mTitleString.IsEmpty()) { |
michael@0 | 544 | mTitleString = mUrlString; |
michael@0 | 545 | } |
michael@0 | 546 | |
michael@0 | 547 | nsCOMPtr<imgIRequest> imgRequest; |
michael@0 | 548 | |
michael@0 | 549 | // grab the image data, and its request. |
michael@0 | 550 | nsCOMPtr<imgIContainer> img = |
michael@0 | 551 | nsContentUtils::GetImageFromContent(image, |
michael@0 | 552 | getter_AddRefs(imgRequest)); |
michael@0 | 553 | |
michael@0 | 554 | nsCOMPtr<nsIMIMEService> mimeService = |
michael@0 | 555 | do_GetService("@mozilla.org/mime;1"); |
michael@0 | 556 | |
michael@0 | 557 | // Fix the file extension in the URL if necessary |
michael@0 | 558 | if (imgRequest && mimeService) { |
michael@0 | 559 | nsCOMPtr<nsIURI> imgUri; |
michael@0 | 560 | imgRequest->GetURI(getter_AddRefs(imgUri)); |
michael@0 | 561 | |
michael@0 | 562 | nsCOMPtr<nsIURL> imgUrl(do_QueryInterface(imgUri)); |
michael@0 | 563 | |
michael@0 | 564 | if (imgUrl) { |
michael@0 | 565 | nsAutoCString extension; |
michael@0 | 566 | imgUrl->GetFileExtension(extension); |
michael@0 | 567 | |
michael@0 | 568 | nsXPIDLCString mimeType; |
michael@0 | 569 | imgRequest->GetMimeType(getter_Copies(mimeType)); |
michael@0 | 570 | |
michael@0 | 571 | nsCOMPtr<nsIMIMEInfo> mimeInfo; |
michael@0 | 572 | mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(), |
michael@0 | 573 | getter_AddRefs(mimeInfo)); |
michael@0 | 574 | |
michael@0 | 575 | if (mimeInfo) { |
michael@0 | 576 | nsAutoCString spec; |
michael@0 | 577 | imgUrl->GetSpec(spec); |
michael@0 | 578 | |
michael@0 | 579 | // pass out the image source string |
michael@0 | 580 | CopyUTF8toUTF16(spec, mImageSourceString); |
michael@0 | 581 | |
michael@0 | 582 | bool validExtension; |
michael@0 | 583 | if (extension.IsEmpty() || |
michael@0 | 584 | NS_FAILED(mimeInfo->ExtensionExists(extension, |
michael@0 | 585 | &validExtension)) || |
michael@0 | 586 | !validExtension) { |
michael@0 | 587 | // Fix the file extension in the URL |
michael@0 | 588 | nsresult rv = imgUrl->Clone(getter_AddRefs(imgUri)); |
michael@0 | 589 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 590 | |
michael@0 | 591 | imgUrl = do_QueryInterface(imgUri); |
michael@0 | 592 | |
michael@0 | 593 | nsAutoCString primaryExtension; |
michael@0 | 594 | mimeInfo->GetPrimaryExtension(primaryExtension); |
michael@0 | 595 | |
michael@0 | 596 | imgUrl->SetFileExtension(primaryExtension); |
michael@0 | 597 | } |
michael@0 | 598 | |
michael@0 | 599 | nsAutoCString fileName; |
michael@0 | 600 | imgUrl->GetFileName(fileName); |
michael@0 | 601 | |
michael@0 | 602 | NS_UnescapeURL(fileName); |
michael@0 | 603 | |
michael@0 | 604 | // make the filename safe for the filesystem |
michael@0 | 605 | fileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, |
michael@0 | 606 | '-'); |
michael@0 | 607 | |
michael@0 | 608 | CopyUTF8toUTF16(fileName, mImageDestFileName); |
michael@0 | 609 | |
michael@0 | 610 | // and the image object |
michael@0 | 611 | mImage = img; |
michael@0 | 612 | } |
michael@0 | 613 | } |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | if (parentLink) { |
michael@0 | 617 | // If we are dragging around an image in an anchor, then we |
michael@0 | 618 | // are dragging the entire anchor |
michael@0 | 619 | linkNode = parentLink; |
michael@0 | 620 | nodeToSerialize = linkNode; |
michael@0 | 621 | } else { |
michael@0 | 622 | nodeToSerialize = do_QueryInterface(draggedNode); |
michael@0 | 623 | } |
michael@0 | 624 | dragNode = nodeToSerialize; |
michael@0 | 625 | } else if (link) { |
michael@0 | 626 | // set linkNode. The code below will handle this |
michael@0 | 627 | linkNode = do_QueryInterface(link); // XXX test this |
michael@0 | 628 | GetNodeString(draggedNode, mTitleString); |
michael@0 | 629 | } else if (parentLink) { |
michael@0 | 630 | // parentLink will always be null if there's selected content |
michael@0 | 631 | linkNode = parentLink; |
michael@0 | 632 | nodeToSerialize = linkNode; |
michael@0 | 633 | } else if (!haveSelectedContent) { |
michael@0 | 634 | // nothing draggable |
michael@0 | 635 | return NS_OK; |
michael@0 | 636 | } |
michael@0 | 637 | |
michael@0 | 638 | if (linkNode) { |
michael@0 | 639 | mIsAnchor = true; |
michael@0 | 640 | GetAnchorURL(linkNode, mUrlString); |
michael@0 | 641 | dragNode = linkNode; |
michael@0 | 642 | } |
michael@0 | 643 | } |
michael@0 | 644 | } |
michael@0 | 645 | |
michael@0 | 646 | if (nodeToSerialize || *aSelection) { |
michael@0 | 647 | mHtmlString.Truncate(); |
michael@0 | 648 | mContextString.Truncate(); |
michael@0 | 649 | mInfoString.Truncate(); |
michael@0 | 650 | mTitleString.Truncate(); |
michael@0 | 651 | |
michael@0 | 652 | nsCOMPtr<nsIDocument> doc = mWindow->GetDoc(); |
michael@0 | 653 | NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); |
michael@0 | 654 | |
michael@0 | 655 | // if we have selected text, use it in preference to the node |
michael@0 | 656 | nsCOMPtr<nsITransferable> transferable; |
michael@0 | 657 | if (*aSelection) { |
michael@0 | 658 | rv = nsCopySupport::GetTransferableForSelection(*aSelection, doc, |
michael@0 | 659 | getter_AddRefs(transferable)); |
michael@0 | 660 | } |
michael@0 | 661 | else { |
michael@0 | 662 | rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc, |
michael@0 | 663 | getter_AddRefs(transferable)); |
michael@0 | 664 | } |
michael@0 | 665 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 666 | |
michael@0 | 667 | nsCOMPtr<nsISupports> supports; |
michael@0 | 668 | nsCOMPtr<nsISupportsString> data; |
michael@0 | 669 | uint32_t dataSize; |
michael@0 | 670 | rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(supports), |
michael@0 | 671 | &dataSize); |
michael@0 | 672 | data = do_QueryInterface(supports); |
michael@0 | 673 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 674 | data->GetData(mHtmlString); |
michael@0 | 675 | } |
michael@0 | 676 | rv = transferable->GetTransferData(kHTMLContext, getter_AddRefs(supports), |
michael@0 | 677 | &dataSize); |
michael@0 | 678 | data = do_QueryInterface(supports); |
michael@0 | 679 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 680 | data->GetData(mContextString); |
michael@0 | 681 | } |
michael@0 | 682 | rv = transferable->GetTransferData(kHTMLInfo, getter_AddRefs(supports), |
michael@0 | 683 | &dataSize); |
michael@0 | 684 | data = do_QueryInterface(supports); |
michael@0 | 685 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 686 | data->GetData(mInfoString); |
michael@0 | 687 | } |
michael@0 | 688 | rv = transferable->GetTransferData(kUnicodeMime, getter_AddRefs(supports), |
michael@0 | 689 | &dataSize); |
michael@0 | 690 | data = do_QueryInterface(supports); |
michael@0 | 691 | NS_ENSURE_SUCCESS(rv, rv); // require plain text at a minimum |
michael@0 | 692 | data->GetData(mTitleString); |
michael@0 | 693 | } |
michael@0 | 694 | |
michael@0 | 695 | // default text value is the URL |
michael@0 | 696 | if (mTitleString.IsEmpty()) { |
michael@0 | 697 | mTitleString = mUrlString; |
michael@0 | 698 | } |
michael@0 | 699 | |
michael@0 | 700 | // if we haven't constructed a html version, make one now |
michael@0 | 701 | if (mHtmlString.IsEmpty() && !mUrlString.IsEmpty()) |
michael@0 | 702 | CreateLinkText(mUrlString, mTitleString, mHtmlString); |
michael@0 | 703 | |
michael@0 | 704 | // if there is no drag node, which will be the case for a selection, just |
michael@0 | 705 | // use the selection target node. |
michael@0 | 706 | rv = AddStringsToDataTransfer( |
michael@0 | 707 | dragNode ? dragNode : mSelectionTargetNode.get(), aDataTransfer); |
michael@0 | 708 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 709 | |
michael@0 | 710 | NS_IF_ADDREF(*aDragNode = dragNode); |
michael@0 | 711 | return NS_OK; |
michael@0 | 712 | } |
michael@0 | 713 | |
michael@0 | 714 | void |
michael@0 | 715 | DragDataProducer::AddString(DataTransfer* aDataTransfer, |
michael@0 | 716 | const nsAString& aFlavor, |
michael@0 | 717 | const nsAString& aData, |
michael@0 | 718 | nsIPrincipal* aPrincipal) |
michael@0 | 719 | { |
michael@0 | 720 | nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID); |
michael@0 | 721 | if (variant) { |
michael@0 | 722 | variant->SetAsAString(aData); |
michael@0 | 723 | aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal); |
michael@0 | 724 | } |
michael@0 | 725 | } |
michael@0 | 726 | |
michael@0 | 727 | nsresult |
michael@0 | 728 | DragDataProducer::AddStringsToDataTransfer(nsIContent* aDragNode, |
michael@0 | 729 | DataTransfer* aDataTransfer) |
michael@0 | 730 | { |
michael@0 | 731 | NS_ASSERTION(aDragNode, "adding strings for null node"); |
michael@0 | 732 | |
michael@0 | 733 | // set all of the data to have the principal of the node where the data came from |
michael@0 | 734 | nsIPrincipal* principal = aDragNode->NodePrincipal(); |
michael@0 | 735 | |
michael@0 | 736 | // add a special flavor if we're an anchor to indicate that we have |
michael@0 | 737 | // a URL in the drag data |
michael@0 | 738 | if (!mUrlString.IsEmpty() && mIsAnchor) { |
michael@0 | 739 | nsAutoString dragData(mUrlString); |
michael@0 | 740 | dragData.AppendLiteral("\n"); |
michael@0 | 741 | // Remove leading and trailing newlines in the title and replace them with |
michael@0 | 742 | // space in remaining positions - they confuse PlacesUtils::unwrapNodes |
michael@0 | 743 | // that expects url\ntitle formatted data for x-moz-url. |
michael@0 | 744 | nsAutoString title(mTitleString); |
michael@0 | 745 | title.Trim("\r\n"); |
michael@0 | 746 | title.ReplaceChar("\r\n", ' '); |
michael@0 | 747 | dragData += title; |
michael@0 | 748 | |
michael@0 | 749 | AddString(aDataTransfer, NS_LITERAL_STRING(kURLMime), dragData, principal); |
michael@0 | 750 | AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal); |
michael@0 | 751 | AddString(aDataTransfer, NS_LITERAL_STRING(kURLDescriptionMime), mTitleString, principal); |
michael@0 | 752 | AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal); |
michael@0 | 753 | } |
michael@0 | 754 | |
michael@0 | 755 | // add a special flavor for the html context data |
michael@0 | 756 | if (!mContextString.IsEmpty()) |
michael@0 | 757 | AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal); |
michael@0 | 758 | |
michael@0 | 759 | // add a special flavor if we have html info data |
michael@0 | 760 | if (!mInfoString.IsEmpty()) |
michael@0 | 761 | AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), mInfoString, principal); |
michael@0 | 762 | |
michael@0 | 763 | // add the full html |
michael@0 | 764 | if (!mHtmlString.IsEmpty()) |
michael@0 | 765 | AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal); |
michael@0 | 766 | |
michael@0 | 767 | // add the plain text. we use the url for text/plain data if an anchor is |
michael@0 | 768 | // being dragged, rather than the title text of the link or the alt text for |
michael@0 | 769 | // an anchor image. |
michael@0 | 770 | AddString(aDataTransfer, NS_LITERAL_STRING(kTextMime), |
michael@0 | 771 | mIsAnchor ? mUrlString : mTitleString, principal); |
michael@0 | 772 | |
michael@0 | 773 | // add image data, if present. For now, all we're going to do with |
michael@0 | 774 | // this is turn it into a native data flavor, so indicate that with |
michael@0 | 775 | // a new flavor so as not to confuse anyone who is really registered |
michael@0 | 776 | // for image/gif or image/jpg. |
michael@0 | 777 | if (mImage) { |
michael@0 | 778 | nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID); |
michael@0 | 779 | if (variant) { |
michael@0 | 780 | variant->SetAsISupports(mImage); |
michael@0 | 781 | aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kNativeImageMime), |
michael@0 | 782 | variant, 0, principal); |
michael@0 | 783 | } |
michael@0 | 784 | |
michael@0 | 785 | // assume the image comes from a file, and add a file promise. We |
michael@0 | 786 | // register ourselves as a nsIFlavorDataProvider, and will use the |
michael@0 | 787 | // GetFlavorData callback to save the image to disk. |
michael@0 | 788 | |
michael@0 | 789 | nsCOMPtr<nsIFlavorDataProvider> dataProvider = |
michael@0 | 790 | new nsContentAreaDragDropDataProvider(); |
michael@0 | 791 | if (dataProvider) { |
michael@0 | 792 | nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID); |
michael@0 | 793 | if (variant) { |
michael@0 | 794 | variant->SetAsISupports(dataProvider); |
michael@0 | 795 | aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kFilePromiseMime), |
michael@0 | 796 | variant, 0, principal); |
michael@0 | 797 | } |
michael@0 | 798 | } |
michael@0 | 799 | |
michael@0 | 800 | AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseURLMime), |
michael@0 | 801 | mImageSourceString, principal); |
michael@0 | 802 | AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseDestFilename), |
michael@0 | 803 | mImageDestFileName, principal); |
michael@0 | 804 | |
michael@0 | 805 | // if not an anchor, add the image url |
michael@0 | 806 | if (!mIsAnchor) { |
michael@0 | 807 | AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal); |
michael@0 | 808 | AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal); |
michael@0 | 809 | } |
michael@0 | 810 | } |
michael@0 | 811 | |
michael@0 | 812 | return NS_OK; |
michael@0 | 813 | } |
michael@0 | 814 | |
michael@0 | 815 | // note that this can return NS_OK, but a null out param (by design) |
michael@0 | 816 | // static |
michael@0 | 817 | nsresult |
michael@0 | 818 | DragDataProducer::GetDraggableSelectionData(nsISelection* inSelection, |
michael@0 | 819 | nsIContent* inRealTargetNode, |
michael@0 | 820 | nsIContent **outImageOrLinkNode, |
michael@0 | 821 | bool* outDragSelectedText) |
michael@0 | 822 | { |
michael@0 | 823 | NS_ENSURE_ARG(inSelection); |
michael@0 | 824 | NS_ENSURE_ARG(inRealTargetNode); |
michael@0 | 825 | NS_ENSURE_ARG_POINTER(outImageOrLinkNode); |
michael@0 | 826 | |
michael@0 | 827 | *outImageOrLinkNode = nullptr; |
michael@0 | 828 | *outDragSelectedText = false; |
michael@0 | 829 | |
michael@0 | 830 | bool selectionContainsTarget = false; |
michael@0 | 831 | |
michael@0 | 832 | bool isCollapsed = false; |
michael@0 | 833 | inSelection->GetIsCollapsed(&isCollapsed); |
michael@0 | 834 | if (!isCollapsed) { |
michael@0 | 835 | nsCOMPtr<nsIDOMNode> realTargetNode = do_QueryInterface(inRealTargetNode); |
michael@0 | 836 | inSelection->ContainsNode(realTargetNode, false, |
michael@0 | 837 | &selectionContainsTarget); |
michael@0 | 838 | |
michael@0 | 839 | if (selectionContainsTarget) { |
michael@0 | 840 | // track down the anchor node, if any, for the url |
michael@0 | 841 | nsCOMPtr<nsIDOMNode> selectionStart; |
michael@0 | 842 | inSelection->GetAnchorNode(getter_AddRefs(selectionStart)); |
michael@0 | 843 | |
michael@0 | 844 | nsCOMPtr<nsIDOMNode> selectionEnd; |
michael@0 | 845 | inSelection->GetFocusNode(getter_AddRefs(selectionEnd)); |
michael@0 | 846 | |
michael@0 | 847 | // look for a selection around a single node, like an image. |
michael@0 | 848 | // in this case, drag the image, rather than a serialization of the HTML |
michael@0 | 849 | // XXX generalize this to other draggable element types? |
michael@0 | 850 | if (selectionStart == selectionEnd) { |
michael@0 | 851 | bool hasChildren; |
michael@0 | 852 | selectionStart->HasChildNodes(&hasChildren); |
michael@0 | 853 | if (hasChildren) { |
michael@0 | 854 | // see if just one node is selected |
michael@0 | 855 | int32_t anchorOffset, focusOffset; |
michael@0 | 856 | inSelection->GetAnchorOffset(&anchorOffset); |
michael@0 | 857 | inSelection->GetFocusOffset(&focusOffset); |
michael@0 | 858 | if (abs(anchorOffset - focusOffset) == 1) { |
michael@0 | 859 | nsCOMPtr<nsIContent> selStartContent = |
michael@0 | 860 | do_QueryInterface(selectionStart); |
michael@0 | 861 | |
michael@0 | 862 | if (selStartContent) { |
michael@0 | 863 | int32_t childOffset = |
michael@0 | 864 | (anchorOffset < focusOffset) ? anchorOffset : focusOffset; |
michael@0 | 865 | nsIContent *childContent = |
michael@0 | 866 | selStartContent->GetChildAt(childOffset); |
michael@0 | 867 | // if we find an image, we'll fall into the node-dragging code, |
michael@0 | 868 | // rather the the selection-dragging code |
michael@0 | 869 | if (nsContentUtils::IsDraggableImage(childContent)) { |
michael@0 | 870 | NS_ADDREF(*outImageOrLinkNode = childContent); |
michael@0 | 871 | return NS_OK; |
michael@0 | 872 | } |
michael@0 | 873 | } |
michael@0 | 874 | } |
michael@0 | 875 | } |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | // see if the selection is a link; if so, its node will be returned |
michael@0 | 879 | GetSelectedLink(inSelection, outImageOrLinkNode); |
michael@0 | 880 | |
michael@0 | 881 | // indicate that a link or text is selected |
michael@0 | 882 | *outDragSelectedText = true; |
michael@0 | 883 | } |
michael@0 | 884 | } |
michael@0 | 885 | |
michael@0 | 886 | return NS_OK; |
michael@0 | 887 | } |
michael@0 | 888 | |
michael@0 | 889 | // static |
michael@0 | 890 | void |
michael@0 | 891 | DragDataProducer::GetSelectedLink(nsISelection* inSelection, |
michael@0 | 892 | nsIContent **outLinkNode) |
michael@0 | 893 | { |
michael@0 | 894 | *outLinkNode = nullptr; |
michael@0 | 895 | |
michael@0 | 896 | nsCOMPtr<nsIDOMNode> selectionStartNode; |
michael@0 | 897 | inSelection->GetAnchorNode(getter_AddRefs(selectionStartNode)); |
michael@0 | 898 | nsCOMPtr<nsIDOMNode> selectionEndNode; |
michael@0 | 899 | inSelection->GetFocusNode(getter_AddRefs(selectionEndNode)); |
michael@0 | 900 | |
michael@0 | 901 | // simple case: only one node is selected |
michael@0 | 902 | // see if it or its parent is an anchor, then exit |
michael@0 | 903 | |
michael@0 | 904 | if (selectionStartNode == selectionEndNode) { |
michael@0 | 905 | nsCOMPtr<nsIContent> selectionStart = do_QueryInterface(selectionStartNode); |
michael@0 | 906 | nsCOMPtr<nsIContent> link = FindParentLinkNode(selectionStart); |
michael@0 | 907 | if (link) { |
michael@0 | 908 | link.swap(*outLinkNode); |
michael@0 | 909 | } |
michael@0 | 910 | |
michael@0 | 911 | return; |
michael@0 | 912 | } |
michael@0 | 913 | |
michael@0 | 914 | // more complicated case: multiple nodes are selected |
michael@0 | 915 | |
michael@0 | 916 | // Unless you use the Alt key while selecting anchor text, it is |
michael@0 | 917 | // nearly impossible to avoid overlapping into adjacent nodes. |
michael@0 | 918 | // Deal with this by trimming off the leading and/or trailing |
michael@0 | 919 | // nodes of the selection if the strings they produce are empty. |
michael@0 | 920 | |
michael@0 | 921 | // first, use a range determine if the selection was marked LTR or RTL; |
michael@0 | 922 | // if the latter, swap endpoints so we trim in the right direction |
michael@0 | 923 | |
michael@0 | 924 | int32_t startOffset, endOffset; |
michael@0 | 925 | { |
michael@0 | 926 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 927 | inSelection->GetRangeAt(0, getter_AddRefs(range)); |
michael@0 | 928 | if (!range) { |
michael@0 | 929 | return; |
michael@0 | 930 | } |
michael@0 | 931 | |
michael@0 | 932 | nsCOMPtr<nsIDOMNode> tempNode; |
michael@0 | 933 | range->GetStartContainer( getter_AddRefs(tempNode)); |
michael@0 | 934 | if (tempNode != selectionStartNode) { |
michael@0 | 935 | selectionEndNode = selectionStartNode; |
michael@0 | 936 | selectionStartNode = tempNode; |
michael@0 | 937 | inSelection->GetAnchorOffset(&endOffset); |
michael@0 | 938 | inSelection->GetFocusOffset(&startOffset); |
michael@0 | 939 | } else { |
michael@0 | 940 | inSelection->GetAnchorOffset(&startOffset); |
michael@0 | 941 | inSelection->GetFocusOffset(&endOffset); |
michael@0 | 942 | } |
michael@0 | 943 | } |
michael@0 | 944 | |
michael@0 | 945 | // trim leading node if the string is empty or |
michael@0 | 946 | // the selection starts at the end of the text |
michael@0 | 947 | |
michael@0 | 948 | nsAutoString nodeStr; |
michael@0 | 949 | selectionStartNode->GetNodeValue(nodeStr); |
michael@0 | 950 | if (nodeStr.IsEmpty() || |
michael@0 | 951 | startOffset+1 >= static_cast<int32_t>(nodeStr.Length())) { |
michael@0 | 952 | nsCOMPtr<nsIDOMNode> curr = selectionStartNode; |
michael@0 | 953 | nsIDOMNode* next; |
michael@0 | 954 | |
michael@0 | 955 | while (curr) { |
michael@0 | 956 | curr->GetNextSibling(&next); |
michael@0 | 957 | |
michael@0 | 958 | if (next) { |
michael@0 | 959 | selectionStartNode = dont_AddRef(next); |
michael@0 | 960 | break; |
michael@0 | 961 | } |
michael@0 | 962 | |
michael@0 | 963 | curr->GetParentNode(&next); |
michael@0 | 964 | curr = dont_AddRef(next); |
michael@0 | 965 | } |
michael@0 | 966 | } |
michael@0 | 967 | |
michael@0 | 968 | // trim trailing node if the selection ends before its text begins |
michael@0 | 969 | |
michael@0 | 970 | if (endOffset == 0) { |
michael@0 | 971 | nsCOMPtr<nsIDOMNode> curr = selectionEndNode; |
michael@0 | 972 | nsIDOMNode* next; |
michael@0 | 973 | |
michael@0 | 974 | while (curr) { |
michael@0 | 975 | curr->GetPreviousSibling(&next); |
michael@0 | 976 | |
michael@0 | 977 | if (next){ |
michael@0 | 978 | selectionEndNode = dont_AddRef(next); |
michael@0 | 979 | break; |
michael@0 | 980 | } |
michael@0 | 981 | |
michael@0 | 982 | curr->GetParentNode(&next); |
michael@0 | 983 | curr = dont_AddRef(next); |
michael@0 | 984 | } |
michael@0 | 985 | } |
michael@0 | 986 | |
michael@0 | 987 | // see if the leading & trailing nodes are part of the |
michael@0 | 988 | // same anchor - if so, return the anchor node |
michael@0 | 989 | nsCOMPtr<nsIContent> selectionStart = do_QueryInterface(selectionStartNode); |
michael@0 | 990 | nsCOMPtr<nsIContent> link = FindParentLinkNode(selectionStart); |
michael@0 | 991 | if (link) { |
michael@0 | 992 | nsCOMPtr<nsIContent> selectionEnd = do_QueryInterface(selectionEndNode); |
michael@0 | 993 | nsCOMPtr<nsIContent> link2 = FindParentLinkNode(selectionEnd); |
michael@0 | 994 | |
michael@0 | 995 | if (link == link2) { |
michael@0 | 996 | NS_IF_ADDREF(*outLinkNode = link); |
michael@0 | 997 | } |
michael@0 | 998 | } |
michael@0 | 999 | |
michael@0 | 1000 | return; |
michael@0 | 1001 | } |