content/base/src/nsContentAreaDragDrop.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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 }

mercurial