123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include <stdio.h>
- #include "mozilla/HTMLEditor.h"
- #include "HTMLEditUtils.h"
- #include "mozilla/Assertions.h"
- #include "mozilla/EditorUtils.h"
- #include "mozilla/dom/Selection.h"
- #include "mozilla/dom/Element.h"
- #include "nsAString.h"
- #include "nsAlgorithm.h"
- #include "nsCOMPtr.h"
- #include "nsDebug.h"
- #include "nsError.h"
- #include "nsGkAtoms.h"
- #include "nsIAtom.h"
- #include "nsIContent.h"
- #include "nsIDOMElement.h"
- #include "nsIDOMNode.h"
- #include "nsIEditor.h"
- #include "nsIFrame.h"
- #include "nsINode.h"
- #include "nsIPresShell.h"
- #include "nsISupportsUtils.h"
- #include "nsITableCellLayout.h" // For efficient access to table cell
- #include "nsITableEditor.h"
- #include "nsLiteralString.h"
- #include "nsQueryFrame.h"
- #include "nsRange.h"
- #include "nsString.h"
- #include "nsTArray.h"
- #include "nsTableCellFrame.h"
- #include "nsTableWrapperFrame.h"
- #include "nscore.h"
- #include <algorithm>
- namespace mozilla {
- using namespace dom;
- /**
- * Stack based helper class for restoring selection after table edit.
- */
- class MOZ_STACK_CLASS AutoSelectionSetterAfterTableEdit final
- {
- private:
- nsCOMPtr<nsITableEditor> mTableEditor;
- nsCOMPtr<nsIDOMElement> mTable;
- int32_t mCol, mRow, mDirection, mSelected;
- public:
- AutoSelectionSetterAfterTableEdit(nsITableEditor* aTableEditor,
- nsIDOMElement* aTable,
- int32_t aRow,
- int32_t aCol,
- int32_t aDirection,
- bool aSelected)
- : mTableEditor(aTableEditor)
- , mTable(aTable)
- , mCol(aCol)
- , mRow(aRow)
- , mDirection(aDirection)
- , mSelected(aSelected)
- {
- }
- ~AutoSelectionSetterAfterTableEdit()
- {
- if (mTableEditor) {
- mTableEditor->SetSelectionAfterTableEdit(mTable, mRow, mCol, mDirection,
- mSelected);
- }
- }
- // This is needed to abort the caret reset in the destructor
- // when one method yields control to another
- void CancelSetCaret()
- {
- mTableEditor = nullptr;
- mTable = nullptr;
- }
- };
- NS_IMETHODIMP
- HTMLEditor::InsertCell(nsIDOMElement* aCell,
- int32_t aRowSpan,
- int32_t aColSpan,
- bool aAfter,
- bool aIsHeader,
- nsIDOMElement** aNewCell)
- {
- NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
- if (aNewCell) {
- *aNewCell = nullptr;
- }
- // And the parent and offsets needed to do an insert
- nsCOMPtr<nsIDOMNode> cellParent;
- nsresult rv = aCell->GetParentNode(getter_AddRefs(cellParent));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(cellParent, NS_ERROR_NULL_POINTER);
- int32_t cellOffset = GetChildOffset(aCell, cellParent);
- nsCOMPtr<nsIDOMElement> newCell;
- rv = CreateElementWithDefaults(aIsHeader ? NS_LITERAL_STRING("th") :
- NS_LITERAL_STRING("tb"),
- getter_AddRefs(newCell));
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!newCell) {
- return NS_ERROR_FAILURE;
- }
- //Optional: return new cell created
- if (aNewCell) {
- *aNewCell = newCell.get();
- NS_ADDREF(*aNewCell);
- }
- if (aRowSpan > 1) {
- // Note: Do NOT use editor transaction for this
- nsAutoString newRowSpan;
- newRowSpan.AppendInt(aRowSpan, 10);
- newCell->SetAttribute(NS_LITERAL_STRING("rowspan"), newRowSpan);
- }
- if (aColSpan > 1) {
- // Note: Do NOT use editor transaction for this
- nsAutoString newColSpan;
- newColSpan.AppendInt(aColSpan, 10);
- newCell->SetAttribute(NS_LITERAL_STRING("colspan"), newColSpan);
- }
- if (aAfter) {
- cellOffset++;
- }
- //Don't let Rules System change the selection
- AutoTransactionsConserveSelection dontChangeSelection(this);
- return InsertNode(newCell, cellParent, cellOffset);
- }
- NS_IMETHODIMP
- HTMLEditor::SetColSpan(nsIDOMElement* aCell,
- int32_t aColSpan)
- {
- NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
- nsAutoString newSpan;
- newSpan.AppendInt(aColSpan, 10);
- return SetAttribute(aCell, NS_LITERAL_STRING("colspan"), newSpan);
- }
- NS_IMETHODIMP
- HTMLEditor::SetRowSpan(nsIDOMElement* aCell,
- int32_t aRowSpan)
- {
- NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
- nsAutoString newSpan;
- newSpan.AppendInt(aRowSpan, 10);
- return SetAttribute(aCell, NS_LITERAL_STRING("rowspan"), newSpan);
- }
- NS_IMETHODIMP
- HTMLEditor::InsertTableCell(int32_t aNumber,
- bool aAfter)
- {
- nsCOMPtr<nsIDOMElement> table;
- nsCOMPtr<nsIDOMElement> curCell;
- nsCOMPtr<nsIDOMNode> cellParent;
- int32_t cellOffset, startRowIndex, startColIndex;
- nsresult rv = GetCellContext(nullptr,
- getter_AddRefs(table),
- getter_AddRefs(curCell),
- getter_AddRefs(cellParent), &cellOffset,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if no cell found
- NS_ENSURE_TRUE(curCell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- // Get more data for current cell in row we are inserting at (we need COLSPAN)
- int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- rv = GetCellDataAt(table, startRowIndex, startColIndex,
- getter_AddRefs(curCell),
- &curStartRowIndex, &curStartColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
- int32_t newCellIndex = aAfter ? (startColIndex+colSpan) : startColIndex;
- //We control selection resetting after the insert...
- AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
- newCellIndex, ePreviousColumn,
- false);
- //...so suppress Rules System selection munging
- AutoTransactionsConserveSelection dontChangeSelection(this);
- for (int32_t i = 0; i < aNumber; i++) {
- nsCOMPtr<nsIDOMElement> newCell;
- rv = CreateElementWithDefaults(NS_LITERAL_STRING("td"),
- getter_AddRefs(newCell));
- if (NS_SUCCEEDED(rv) && newCell) {
- if (aAfter) {
- cellOffset++;
- }
- rv = InsertNode(newCell, cellParent, cellOffset);
- if (NS_FAILED(rv)) {
- break;
- }
- }
- }
- // XXX This is perhaps the result of the last call of InsertNode() or
- // CreateElementWithDefaults().
- return rv;
- }
- NS_IMETHODIMP
- HTMLEditor::GetFirstRow(nsIDOMElement* aTableElement,
- nsIDOMNode** aRowNode)
- {
- NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER);
- *aRowNode = nullptr;
- NS_ENSURE_TRUE(aTableElement, NS_ERROR_NULL_POINTER);
- nsCOMPtr<nsIDOMElement> tableElement;
- nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"),
- aTableElement,
- getter_AddRefs(tableElement));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(tableElement, NS_ERROR_NULL_POINTER);
- nsCOMPtr<nsIDOMNode> tableChild;
- rv = tableElement->GetFirstChild(getter_AddRefs(tableChild));
- NS_ENSURE_SUCCESS(rv, rv);
- while (tableChild) {
- nsCOMPtr<nsIContent> content = do_QueryInterface(tableChild);
- if (content) {
- if (content->IsHTMLElement(nsGkAtoms::tr)) {
- // Found a row directly under <table>
- *aRowNode = tableChild;
- NS_ADDREF(*aRowNode);
- return NS_OK;
- }
- // Look for row in one of the row container elements
- if (content->IsAnyOfHTMLElements(nsGkAtoms::tbody,
- nsGkAtoms::thead,
- nsGkAtoms::tfoot)) {
- nsCOMPtr<nsIDOMNode> rowNode;
- rv = tableChild->GetFirstChild(getter_AddRefs(rowNode));
- NS_ENSURE_SUCCESS(rv, rv);
- // We can encounter textnodes here -- must find a row
- while (rowNode && !HTMLEditUtils::IsTableRow(rowNode)) {
- nsCOMPtr<nsIDOMNode> nextNode;
- rv = rowNode->GetNextSibling(getter_AddRefs(nextNode));
- NS_ENSURE_SUCCESS(rv, rv);
- rowNode = nextNode;
- }
- if (rowNode) {
- *aRowNode = rowNode.get();
- NS_ADDREF(*aRowNode);
- return NS_OK;
- }
- }
- }
- // Here if table child was a CAPTION or COLGROUP
- // or child of a row parent wasn't a row (bad HTML?),
- // or first child was a textnode
- // Look in next table child
- nsCOMPtr<nsIDOMNode> nextChild;
- rv = tableChild->GetNextSibling(getter_AddRefs(nextChild));
- NS_ENSURE_SUCCESS(rv, rv);
- tableChild = nextChild;
- }
- // If here, row was not found
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- NS_IMETHODIMP
- HTMLEditor::GetNextRow(nsIDOMNode* aCurrentRowNode,
- nsIDOMNode** aRowNode)
- {
- NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER);
- *aRowNode = nullptr;
- NS_ENSURE_TRUE(aCurrentRowNode, NS_ERROR_NULL_POINTER);
- if (!HTMLEditUtils::IsTableRow(aCurrentRowNode)) {
- return NS_ERROR_FAILURE;
- }
- nsCOMPtr<nsIDOMNode> nextRow;
- nsresult rv = aCurrentRowNode->GetNextSibling(getter_AddRefs(nextRow));
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIDOMNode> nextNode;
- // Skip over any textnodes here
- while (nextRow && !HTMLEditUtils::IsTableRow(nextRow)) {
- rv = nextRow->GetNextSibling(getter_AddRefs(nextNode));
- NS_ENSURE_SUCCESS(rv, rv);
- nextRow = nextNode;
- }
- if (nextRow) {
- *aRowNode = nextRow.get();
- NS_ADDREF(*aRowNode);
- return NS_OK;
- }
- // No row found, search for rows in other table sections
- nsCOMPtr<nsIDOMNode> rowParent;
- rv = aCurrentRowNode->GetParentNode(getter_AddRefs(rowParent));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(rowParent, NS_ERROR_NULL_POINTER);
- nsCOMPtr<nsIDOMNode> parentSibling;
- rv = rowParent->GetNextSibling(getter_AddRefs(parentSibling));
- NS_ENSURE_SUCCESS(rv, rv);
- while (parentSibling) {
- rv = parentSibling->GetFirstChild(getter_AddRefs(nextRow));
- NS_ENSURE_SUCCESS(rv, rv);
- // We can encounter textnodes here -- must find a row
- while (nextRow && !HTMLEditUtils::IsTableRow(nextRow)) {
- rv = nextRow->GetNextSibling(getter_AddRefs(nextNode));
- NS_ENSURE_SUCCESS(rv, rv);
- nextRow = nextNode;
- }
- if (nextRow) {
- *aRowNode = nextRow.get();
- NS_ADDREF(*aRowNode);
- return NS_OK;
- }
- // We arrive here only if a table section has no children
- // or first child of section is not a row (bad HTML or more "_moz_text" nodes!)
- // So look for another section sibling
- rv = parentSibling->GetNextSibling(getter_AddRefs(nextNode));
- NS_ENSURE_SUCCESS(rv, rv);
- parentSibling = nextNode;
- }
- // If here, row was not found
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- nsresult
- HTMLEditor::GetLastCellInRow(nsIDOMNode* aRowNode,
- nsIDOMNode** aCellNode)
- {
- NS_ENSURE_TRUE(aCellNode, NS_ERROR_NULL_POINTER);
- *aCellNode = nullptr;
- NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER);
- nsCOMPtr<nsIDOMNode> rowChild;
- nsresult rv = aRowNode->GetLastChild(getter_AddRefs(rowChild));
- NS_ENSURE_SUCCESS(rv, rv);
- while (rowChild && !HTMLEditUtils::IsTableCell(rowChild)) {
- // Skip over textnodes
- nsCOMPtr<nsIDOMNode> previousChild;
- rv = rowChild->GetPreviousSibling(getter_AddRefs(previousChild));
- NS_ENSURE_SUCCESS(rv, rv);
- rowChild = previousChild;
- }
- if (rowChild) {
- *aCellNode = rowChild.get();
- NS_ADDREF(*aCellNode);
- return NS_OK;
- }
- // If here, cell was not found
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- NS_IMETHODIMP
- HTMLEditor::InsertTableColumn(int32_t aNumber,
- bool aAfter)
- {
- RefPtr<Selection> selection;
- nsCOMPtr<nsIDOMElement> table;
- nsCOMPtr<nsIDOMElement> curCell;
- int32_t startRowIndex, startColIndex;
- nsresult rv = GetCellContext(getter_AddRefs(selection),
- getter_AddRefs(table),
- getter_AddRefs(curCell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if no cell found
- NS_ENSURE_TRUE(curCell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- // Get more data for current cell (we need ROWSPAN)
- int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- rv = GetCellDataAt(table, startRowIndex, startColIndex,
- getter_AddRefs(curCell),
- &curStartRowIndex, &curStartColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
- AutoEditBatch beginBatching(this);
- // Prevent auto insertion of BR in new cell until we're done
- AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
- // Use column after current cell if requested
- if (aAfter) {
- startColIndex += actualColSpan;
- //Detect when user is adding after a COLSPAN=0 case
- // Assume they want to stop the "0" behavior and
- // really add a new column. Thus we set the
- // colspan to its true value
- if (!colSpan) {
- SetColSpan(curCell, actualColSpan);
- }
- }
- int32_t rowCount, colCount, rowIndex;
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- //We reset caret in destructor...
- AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
- startColIndex, ePreviousRow,
- false);
- //.. so suppress Rules System selection munging
- AutoTransactionsConserveSelection dontChangeSelection(this);
- // If we are inserting after all existing columns
- // Make sure table is "well formed"
- // before appending new column
- if (startColIndex >= colCount) {
- NormalizeTable(table);
- }
- nsCOMPtr<nsIDOMNode> rowNode;
- for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
- if (startColIndex < colCount) {
- // We are inserting before an existing column
- rv = GetCellDataAt(table, rowIndex, startColIndex,
- getter_AddRefs(curCell),
- &curStartRowIndex, &curStartColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail entire process if we fail to find a cell
- // (may fail just in particular rows with < adequate cells per row)
- if (curCell) {
- if (curStartColIndex < startColIndex) {
- // We have a cell spanning this location
- // Simply increase its colspan to keep table rectangular
- // Note: we do nothing if colsSpan=0,
- // since it should automatically span the new column
- if (colSpan > 0) {
- SetColSpan(curCell, colSpan+aNumber);
- }
- } else {
- // Simply set selection to the current cell
- // so we can let InsertTableCell() do the work
- // Insert a new cell before current one
- selection->Collapse(curCell, 0);
- rv = InsertTableCell(aNumber, false);
- }
- }
- } else {
- // Get current row and append new cells after last cell in row
- if (!rowIndex) {
- rv = GetFirstRow(table.get(), getter_AddRefs(rowNode));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- } else {
- nsCOMPtr<nsIDOMNode> nextRow;
- rv = GetNextRow(rowNode.get(), getter_AddRefs(nextRow));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rowNode = nextRow;
- }
- if (rowNode) {
- nsCOMPtr<nsIDOMNode> lastCell;
- rv = GetLastCellInRow(rowNode, getter_AddRefs(lastCell));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(lastCell, NS_ERROR_FAILURE);
- curCell = do_QueryInterface(lastCell);
- if (curCell) {
- // Simply add same number of cells to each row
- // Although tempted to check cell indexes for curCell,
- // the effects of COLSPAN>1 in some cells makes this futile!
- // We must use NormalizeTable first to assure
- // that there are cells in each cellmap location
- selection->Collapse(curCell, 0);
- rv = InsertTableCell(aNumber, true);
- }
- }
- }
- }
- // XXX This is perhaps the result of the last call of InsertTableCell().
- return rv;
- }
- NS_IMETHODIMP
- HTMLEditor::InsertTableRow(int32_t aNumber,
- bool aAfter)
- {
- nsCOMPtr<nsIDOMElement> table;
- nsCOMPtr<nsIDOMElement> curCell;
- int32_t startRowIndex, startColIndex;
- nsresult rv = GetCellContext(nullptr,
- getter_AddRefs(table),
- getter_AddRefs(curCell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if no cell found
- NS_ENSURE_TRUE(curCell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- // Get more data for current cell in row we are inserting at (we need COLSPAN)
- int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- rv = GetCellDataAt(table, startRowIndex, startColIndex,
- getter_AddRefs(curCell),
- &curStartRowIndex, &curStartColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
- int32_t rowCount, colCount;
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- AutoEditBatch beginBatching(this);
- // Prevent auto insertion of BR in new cell until we're done
- AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
- if (aAfter) {
- // Use row after current cell
- startRowIndex += actualRowSpan;
- //Detect when user is adding after a ROWSPAN=0 case
- // Assume they want to stop the "0" behavior and
- // really add a new row. Thus we set the
- // rowspan to its true value
- if (!rowSpan) {
- SetRowSpan(curCell, actualRowSpan);
- }
- }
- //We control selection resetting after the insert...
- AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
- startColIndex, ePreviousColumn,
- false);
- //...so suppress Rules System selection munging
- AutoTransactionsConserveSelection dontChangeSelection(this);
- nsCOMPtr<nsIDOMElement> cellForRowParent;
- int32_t cellsInRow = 0;
- if (startRowIndex < rowCount) {
- // We are inserting above an existing row
- // Get each cell in the insert row to adjust for COLSPAN effects while we
- // count how many cells are needed
- int32_t colIndex = 0;
- while (NS_SUCCEEDED(GetCellDataAt(table, startRowIndex, colIndex,
- getter_AddRefs(curCell),
- &curStartRowIndex, &curStartColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan,
- &isSelected))) {
- if (curCell) {
- if (curStartRowIndex < startRowIndex) {
- // We have a cell spanning this location
- // Simply increase its rowspan
- //Note that if rowSpan == 0, we do nothing,
- // since that cell should automatically extend into the new row
- if (rowSpan > 0) {
- SetRowSpan(curCell, rowSpan+aNumber);
- }
- } else {
- // We have a cell in the insert row
- // Count the number of cells we need to add to the new row
- cellsInRow += actualColSpan;
- // Save cell we will use below
- if (!cellForRowParent) {
- cellForRowParent = curCell;
- }
- }
- // Next cell in row
- colIndex += actualColSpan;
- } else {
- colIndex++;
- }
- }
- } else {
- // We are adding a new row after all others
- // If it weren't for colspan=0 effect,
- // we could simply use colCount for number of new cells...
- // XXX colspan=0 support has now been removed in table layout so maybe this can be cleaned up now? (bug 1243183)
- cellsInRow = colCount;
- // ...but we must compensate for all cells with rowSpan = 0 in the last row
- int32_t lastRow = rowCount-1;
- int32_t tempColIndex = 0;
- while (NS_SUCCEEDED(GetCellDataAt(table, lastRow, tempColIndex,
- getter_AddRefs(curCell),
- &curStartRowIndex, &curStartColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan,
- &isSelected))) {
- if (!rowSpan) {
- cellsInRow -= actualColSpan;
- }
- tempColIndex += actualColSpan;
- // Save cell from the last row that we will use below
- if (!cellForRowParent && curStartRowIndex == lastRow) {
- cellForRowParent = curCell;
- }
- }
- }
- if (cellsInRow > 0) {
- // The row parent and offset where we will insert new row
- nsCOMPtr<nsIDOMNode> parentOfRow;
- int32_t newRowOffset;
- NS_NAMED_LITERAL_STRING(trStr, "tr");
- if (!cellForRowParent) {
- return NS_ERROR_FAILURE;
- }
- nsCOMPtr<nsIDOMElement> parentRow;
- rv = GetElementOrParentByTagName(trStr, cellForRowParent,
- getter_AddRefs(parentRow));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER);
- parentRow->GetParentNode(getter_AddRefs(parentOfRow));
- NS_ENSURE_TRUE(parentOfRow, NS_ERROR_NULL_POINTER);
- newRowOffset = GetChildOffset(parentRow, parentOfRow);
- // Adjust for when adding past the end
- if (aAfter && startRowIndex >= rowCount) {
- newRowOffset++;
- }
- for (int32_t row = 0; row < aNumber; row++) {
- // Create a new row
- nsCOMPtr<nsIDOMElement> newRow;
- rv = CreateElementWithDefaults(trStr, getter_AddRefs(newRow));
- if (NS_SUCCEEDED(rv)) {
- NS_ENSURE_TRUE(newRow, NS_ERROR_FAILURE);
- for (int32_t i = 0; i < cellsInRow; i++) {
- nsCOMPtr<nsIDOMElement> newCell;
- rv = CreateElementWithDefaults(NS_LITERAL_STRING("td"),
- getter_AddRefs(newCell));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(newCell, NS_ERROR_FAILURE);
- // Don't use transaction system yet! (not until entire row is inserted)
- nsCOMPtr<nsIDOMNode>resultNode;
- rv = newRow->AppendChild(newCell, getter_AddRefs(resultNode));
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Use transaction system to insert the entire row+cells
- // (Note that rows are inserted at same childoffset each time)
- rv = InsertNode(newRow, parentOfRow, newRowOffset);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- }
- // XXX This might be the result of the last call of
- // CreateElementWithDefaults(), otherwise, NS_OK.
- return rv;
- }
- // Editor helper only
- // XXX Code changed for bug 217717 and now we don't need aSelection param
- // TODO: Remove aSelection param
- nsresult
- HTMLEditor::DeleteTable2(nsIDOMElement* aTable,
- Selection* aSelection)
- {
- NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
- // Select the table
- nsresult rv = ClearSelection();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- rv = AppendNodeToSelectionAsRange(aTable);
- NS_ENSURE_SUCCESS(rv, rv);
- return DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
- }
- NS_IMETHODIMP
- HTMLEditor::DeleteTable()
- {
- RefPtr<Selection> selection;
- nsCOMPtr<nsIDOMElement> table;
- nsresult rv = GetCellContext(getter_AddRefs(selection),
- getter_AddRefs(table),
- nullptr, nullptr, nullptr, nullptr, nullptr);
- NS_ENSURE_SUCCESS(rv, rv);
- AutoEditBatch beginBatching(this);
- return DeleteTable2(table, selection);
- }
- NS_IMETHODIMP
- HTMLEditor::DeleteTableCell(int32_t aNumber)
- {
- RefPtr<Selection> selection;
- nsCOMPtr<nsIDOMElement> table;
- nsCOMPtr<nsIDOMElement> cell;
- int32_t startRowIndex, startColIndex;
- nsresult rv = GetCellContext(getter_AddRefs(selection),
- getter_AddRefs(table),
- getter_AddRefs(cell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if we didn't find a table or cell
- NS_ENSURE_TRUE(table && cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- AutoEditBatch beginBatching(this);
- // Prevent rules testing until we're done
- AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
- nsCOMPtr<nsIDOMElement> firstCell;
- nsCOMPtr<nsIDOMRange> range;
- rv = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
- NS_ENSURE_SUCCESS(rv, rv);
- int32_t rangeCount;
- rv = selection->GetRangeCount(&rangeCount);
- NS_ENSURE_SUCCESS(rv, rv);
- if (firstCell && rangeCount > 1) {
- // When > 1 selected cell,
- // ignore aNumber and use selected cells
- cell = firstCell;
- int32_t rowCount, colCount;
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- // Get indexes -- may be different than original cell
- rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- // The setCaret object will call AutoSelectionSetterAfterTableEdit in its
- // destructor
- AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
- startColIndex, ePreviousColumn,
- false);
- AutoTransactionsConserveSelection dontChangeSelection(this);
- bool checkToDeleteRow = true;
- bool checkToDeleteColumn = true;
- while (cell) {
- bool deleteRow = false;
- bool deleteCol = false;
- if (checkToDeleteRow) {
- // Optimize to delete an entire row
- // Clear so we don't repeat AllCellsInRowSelected within the same row
- checkToDeleteRow = false;
- deleteRow = AllCellsInRowSelected(table, startRowIndex, colCount);
- if (deleteRow) {
- // First, find the next cell in a different row
- // to continue after we delete this row
- int32_t nextRow = startRowIndex;
- while (nextRow == startRowIndex) {
- rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!cell) {
- break;
- }
- rv = GetCellIndexes(cell, &nextRow, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Delete entire row
- rv = DeleteRow(table, startRowIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- if (cell) {
- // For the next cell: Subtract 1 for row we deleted
- startRowIndex = nextRow - 1;
- // Set true since we know we will look at a new row next
- checkToDeleteRow = true;
- }
- }
- }
- if (!deleteRow) {
- if (checkToDeleteColumn) {
- // Optimize to delete an entire column
- // Clear this so we don't repeat AllCellsInColSelected within the same Col
- checkToDeleteColumn = false;
- deleteCol = AllCellsInColumnSelected(table, startColIndex, colCount);
- if (deleteCol) {
- // First, find the next cell in a different column
- // to continue after we delete this column
- int32_t nextCol = startColIndex;
- while (nextCol == startColIndex) {
- rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!cell) {
- break;
- }
- rv = GetCellIndexes(cell, &startRowIndex, &nextCol);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Delete entire Col
- rv = DeleteColumn(table, startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- if (cell) {
- // For the next cell, subtract 1 for col. deleted
- startColIndex = nextCol - 1;
- // Set true since we know we will look at a new column next
- checkToDeleteColumn = true;
- }
- }
- }
- if (!deleteCol) {
- // First get the next cell to delete
- nsCOMPtr<nsIDOMElement> nextCell;
- rv = GetNextSelectedCell(getter_AddRefs(range),
- getter_AddRefs(nextCell));
- NS_ENSURE_SUCCESS(rv, rv);
- // Then delete the cell
- rv = DeleteNode(cell);
- NS_ENSURE_SUCCESS(rv, rv);
- // The next cell to delete
- cell = nextCell;
- if (cell) {
- rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- }
- }
- } else {
- for (int32_t i = 0; i < aNumber; i++) {
- rv = GetCellContext(getter_AddRefs(selection),
- getter_AddRefs(table),
- getter_AddRefs(cell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if no cell found
- NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- if (GetNumberOfCellsInRow(table, startRowIndex) == 1) {
- nsCOMPtr<nsIDOMElement> parentRow;
- rv = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell,
- getter_AddRefs(parentRow));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER);
- // We should delete the row instead,
- // but first check if its the only row left
- // so we can delete the entire table
- int32_t rowCount, colCount;
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- if (rowCount == 1) {
- return DeleteTable2(table, selection);
- }
- // We need to call DeleteTableRow to handle cells with rowspan
- rv = DeleteTableRow(1);
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- // More than 1 cell in the row
- // The setCaret object will call AutoSelectionSetterAfterTableEdit in its
- // destructor
- AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
- startColIndex, ePreviousColumn,
- false);
- AutoTransactionsConserveSelection dontChangeSelection(this);
- rv = DeleteNode(cell);
- // If we fail, don't try to delete any more cells???
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::DeleteTableCellContents()
- {
- RefPtr<Selection> selection;
- nsCOMPtr<nsIDOMElement> table;
- nsCOMPtr<nsIDOMElement> cell;
- int32_t startRowIndex, startColIndex;
- nsresult rv = GetCellContext(getter_AddRefs(selection),
- getter_AddRefs(table),
- getter_AddRefs(cell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if no cell found
- NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- AutoEditBatch beginBatching(this);
- // Prevent rules testing until we're done
- AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
- //Don't let Rules System change the selection
- AutoTransactionsConserveSelection dontChangeSelection(this);
- nsCOMPtr<nsIDOMElement> firstCell;
- nsCOMPtr<nsIDOMRange> range;
- rv = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
- NS_ENSURE_SUCCESS(rv, rv);
- if (firstCell) {
- cell = firstCell;
- rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
- startColIndex, ePreviousColumn,
- false);
- while (cell) {
- DeleteCellContents(cell);
- if (firstCell) {
- // We doing a selected cells, so do all of them
- rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- cell = nullptr;
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::DeleteCellContents(nsIDOMElement* aCell)
- {
- NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
- // Prevent rules testing until we're done
- AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
- nsCOMPtr<nsIDOMNode> child;
- bool hasChild;
- aCell->HasChildNodes(&hasChild);
- while (hasChild) {
- aCell->GetLastChild(getter_AddRefs(child));
- nsresult rv = DeleteNode(child);
- NS_ENSURE_SUCCESS(rv, rv);
- aCell->HasChildNodes(&hasChild);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::DeleteTableColumn(int32_t aNumber)
- {
- RefPtr<Selection> selection;
- nsCOMPtr<nsIDOMElement> table;
- nsCOMPtr<nsIDOMElement> cell;
- int32_t startRowIndex, startColIndex, rowCount, colCount;
- nsresult rv = GetCellContext(getter_AddRefs(selection),
- getter_AddRefs(table),
- getter_AddRefs(cell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if no cell found
- NS_ENSURE_TRUE(table && cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- // Shortcut the case of deleting all columns in table
- if (!startColIndex && aNumber >= colCount) {
- return DeleteTable2(table, selection);
- }
- // Check for counts too high
- aNumber = std::min(aNumber,(colCount-startColIndex));
- AutoEditBatch beginBatching(this);
- // Prevent rules testing until we're done
- AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
- // Test if deletion is controlled by selected cells
- nsCOMPtr<nsIDOMElement> firstCell;
- nsCOMPtr<nsIDOMRange> range;
- rv = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
- NS_ENSURE_SUCCESS(rv, rv);
- int32_t rangeCount;
- rv = selection->GetRangeCount(&rangeCount);
- NS_ENSURE_SUCCESS(rv, rv);
- if (firstCell && rangeCount > 1) {
- // Fetch indexes again - may be different for selected cells
- rv = GetCellIndexes(firstCell, &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- //We control selection resetting after the insert...
- AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
- startColIndex, ePreviousRow,
- false);
- if (firstCell && rangeCount > 1) {
- // Use selected cells to determine what rows to delete
- cell = firstCell;
- while (cell) {
- if (cell != firstCell) {
- rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Find the next cell in a different column
- // to continue after we delete this column
- int32_t nextCol = startColIndex;
- while (nextCol == startColIndex) {
- rv = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!cell) {
- break;
- }
- rv = GetCellIndexes(cell, &startRowIndex, &nextCol);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- rv = DeleteColumn(table, startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- } else {
- for (int32_t i = 0; i < aNumber; i++) {
- rv = DeleteColumn(table, startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::DeleteColumn(nsIDOMElement* aTable,
- int32_t aColIndex)
- {
- NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
- nsCOMPtr<nsIDOMElement> cell;
- int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- int32_t rowIndex = 0;
- do {
- nsresult rv =
- GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell),
- &startRowIndex, &startColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, rv);
- if (cell) {
- // Find cells that don't start in column we are deleting
- if (startColIndex < aColIndex || colSpan > 1 || !colSpan) {
- // We have a cell spanning this location
- // Decrease its colspan to keep table rectangular,
- // but if colSpan=0, it will adjust automatically
- if (colSpan > 0) {
- NS_ASSERTION((colSpan > 1),"Bad COLSPAN in DeleteTableColumn");
- SetColSpan(cell, colSpan-1);
- }
- if (startColIndex == aColIndex) {
- // Cell is in column to be deleted, but must have colspan > 1,
- // so delete contents of cell instead of cell itself
- // (We must have reset colspan above)
- DeleteCellContents(cell);
- }
- // To next cell in column
- rowIndex += actualRowSpan;
- } else {
- // Delete the cell
- if (GetNumberOfCellsInRow(aTable, rowIndex) == 1) {
- // Only 1 cell in row - delete the row
- nsCOMPtr<nsIDOMElement> parentRow;
- rv = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell,
- getter_AddRefs(parentRow));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!parentRow) {
- return NS_ERROR_NULL_POINTER;
- }
- // But first check if its the only row left
- // so we can delete the entire table
- // (This should never happen but it's the safe thing to do)
- int32_t rowCount, colCount;
- rv = GetTableSize(aTable, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- if (rowCount == 1) {
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
- return DeleteTable2(aTable, selection);
- }
- // Delete the row by placing caret in cell we were to delete
- // We need to call DeleteTableRow to handle cells with rowspan
- rv = DeleteRow(aTable, startRowIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- // Note that we don't incremenet rowIndex
- // since a row was deleted and "next"
- // row now has current rowIndex
- } else {
- // A more "normal" deletion
- rv = DeleteNode(cell);
- NS_ENSURE_SUCCESS(rv, rv);
- //Skip over any rows spanned by this cell
- rowIndex += actualRowSpan;
- }
- }
- }
- } while (cell);
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::DeleteTableRow(int32_t aNumber)
- {
- RefPtr<Selection> selection;
- nsCOMPtr<nsIDOMElement> table;
- nsCOMPtr<nsIDOMElement> cell;
- int32_t startRowIndex, startColIndex;
- int32_t rowCount, colCount;
- nsresult rv = GetCellContext(getter_AddRefs(selection),
- getter_AddRefs(table),
- getter_AddRefs(cell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if no cell found
- NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- // Shortcut the case of deleting all rows in table
- if (!startRowIndex && aNumber >= rowCount) {
- return DeleteTable2(table, selection);
- }
- AutoEditBatch beginBatching(this);
- // Prevent rules testing until we're done
- AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
- nsCOMPtr<nsIDOMElement> firstCell;
- nsCOMPtr<nsIDOMRange> range;
- rv = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
- NS_ENSURE_SUCCESS(rv, rv);
- int32_t rangeCount;
- rv = selection->GetRangeCount(&rangeCount);
- NS_ENSURE_SUCCESS(rv, rv);
- if (firstCell && rangeCount > 1) {
- // Fetch indexes again - may be different for selected cells
- rv = GetCellIndexes(firstCell, &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- //We control selection resetting after the insert...
- AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
- startColIndex, ePreviousRow,
- false);
- // Don't change selection during deletions
- AutoTransactionsConserveSelection dontChangeSelection(this);
- if (firstCell && rangeCount > 1) {
- // Use selected cells to determine what rows to delete
- cell = firstCell;
- while (cell) {
- if (cell != firstCell) {
- rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Find the next cell in a different row
- // to continue after we delete this row
- int32_t nextRow = startRowIndex;
- while (nextRow == startRowIndex) {
- rv = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!cell) break;
- rv = GetCellIndexes(cell, &nextRow, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Delete entire row
- rv = DeleteRow(table, startRowIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- } else {
- // Check for counts too high
- aNumber = std::min(aNumber,(rowCount-startRowIndex));
- for (int32_t i = 0; i < aNumber; i++) {
- rv = DeleteRow(table, startRowIndex);
- // If failed in current row, try the next
- if (NS_FAILED(rv)) {
- startRowIndex++;
- }
- // Check if there's a cell in the "next" row
- rv = GetCellAt(table, startRowIndex, startColIndex, getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!cell) {
- break;
- }
- }
- }
- return NS_OK;
- }
- // Helper that doesn't batch or change the selection
- NS_IMETHODIMP
- HTMLEditor::DeleteRow(nsIDOMElement* aTable,
- int32_t aRowIndex)
- {
- NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
- nsCOMPtr<nsIDOMElement> cell;
- nsCOMPtr<nsIDOMElement> cellInDeleteRow;
- int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- int32_t colIndex = 0;
- // Prevent rules testing until we're done
- AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
- // The list of cells we will change rowspan in
- // and the new rowspan values for each
- nsTArray<nsCOMPtr<nsIDOMElement> > spanCellList;
- nsTArray<int32_t> newSpanList;
- int32_t rowCount, colCount;
- nsresult rv = GetTableSize(aTable, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- // Scan through cells in row to do rowspan adjustments
- // Note that after we delete row, startRowIndex will point to the
- // cells in the next row to be deleted
- do {
- if (aRowIndex >= rowCount || colIndex >= colCount) {
- break;
- }
- rv = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell),
- &startRowIndex, &startColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- // We don't fail if we don't find a cell, so this must be real bad
- if (NS_FAILED(rv)) {
- return rv;
- }
- // Compensate for cells that don't start or extend below the row we are deleting
- if (cell) {
- if (startRowIndex < aRowIndex) {
- // Cell starts in row above us
- // Decrease its rowspan to keep table rectangular
- // but we don't need to do this if rowspan=0,
- // since it will automatically adjust
- if (rowSpan > 0) {
- // Build list of cells to change rowspan
- // We can't do it now since it upsets cell map,
- // so we will do it after deleting the row
- spanCellList.AppendElement(cell);
- newSpanList.AppendElement(std::max((aRowIndex - startRowIndex), actualRowSpan-1));
- }
- } else {
- if (rowSpan > 1) {
- // Cell spans below row to delete, so we must insert new cells to
- // keep rows below. Note that we test "rowSpan" so we don't do this
- // if rowSpan = 0 (automatic readjustment).
- int32_t aboveRowToInsertNewCellInto = aRowIndex - startRowIndex + 1;
- int32_t numOfRawSpanRemainingBelow = actualRowSpan - 1;
- rv = SplitCellIntoRows(aTable, startRowIndex, startColIndex,
- aboveRowToInsertNewCellInto,
- numOfRawSpanRemainingBelow, nullptr);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- if (!cellInDeleteRow) {
- cellInDeleteRow = cell; // Reference cell to find row to delete
- }
- }
- // Skip over other columns spanned by this cell
- colIndex += actualColSpan;
- }
- } while (cell);
- // Things are messed up if we didn't find a cell in the row!
- NS_ENSURE_TRUE(cellInDeleteRow, NS_ERROR_FAILURE);
- // Delete the entire row
- nsCOMPtr<nsIDOMElement> parentRow;
- rv = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cellInDeleteRow,
- getter_AddRefs(parentRow));
- NS_ENSURE_SUCCESS(rv, rv);
- if (parentRow) {
- rv = DeleteNode(parentRow);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Now we can set new rowspans for cells stored above
- for (uint32_t i = 0, n = spanCellList.Length(); i < n; i++) {
- nsIDOMElement *cellPtr = spanCellList[i];
- if (cellPtr) {
- rv = SetRowSpan(cellPtr, newSpanList[i]);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::SelectTable()
- {
- nsCOMPtr<nsIDOMElement> table;
- nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr,
- getter_AddRefs(table));
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if we didn't find a table
- NS_ENSURE_TRUE(table, NS_OK);
- rv = ClearSelection();
- if (NS_FAILED(rv)) {
- return rv;
- }
- return AppendNodeToSelectionAsRange(table);
- }
- NS_IMETHODIMP
- HTMLEditor::SelectTableCell()
- {
- nsCOMPtr<nsIDOMElement> cell;
- nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr,
- getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- rv = ClearSelection();
- if (NS_FAILED(rv)) {
- return rv;
- }
- return AppendNodeToSelectionAsRange(cell);
- }
- NS_IMETHODIMP
- HTMLEditor::SelectBlockOfCells(nsIDOMElement* aStartCell,
- nsIDOMElement* aEndCell)
- {
- NS_ENSURE_TRUE(aStartCell && aEndCell, NS_ERROR_NULL_POINTER);
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
- NS_NAMED_LITERAL_STRING(tableStr, "table");
- nsCOMPtr<nsIDOMElement> table;
- nsresult rv = GetElementOrParentByTagName(tableStr, aStartCell,
- getter_AddRefs(table));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
- nsCOMPtr<nsIDOMElement> endTable;
- rv = GetElementOrParentByTagName(tableStr, aEndCell,
- getter_AddRefs(endTable));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(endTable, NS_ERROR_FAILURE);
- // We can only select a block if within the same table,
- // so do nothing if not within one table
- if (table != endTable) {
- return NS_OK;
- }
- int32_t startRowIndex, startColIndex, endRowIndex, endColIndex;
- // Get starting and ending cells' location in the cellmap
- rv = GetCellIndexes(aStartCell, &startRowIndex, &startColIndex);
- if (NS_FAILED(rv)) {
- return rv;
- }
- rv = GetCellIndexes(aEndCell, &endRowIndex, &endColIndex);
- if (NS_FAILED(rv)) {
- return rv;
- }
- // Suppress nsISelectionListener notification
- // until all selection changes are finished
- SelectionBatcher selectionBatcher(selection);
- // Examine all cell nodes in current selection and
- // remove those outside the new block cell region
- int32_t minColumn = std::min(startColIndex, endColIndex);
- int32_t minRow = std::min(startRowIndex, endRowIndex);
- int32_t maxColumn = std::max(startColIndex, endColIndex);
- int32_t maxRow = std::max(startRowIndex, endRowIndex);
- nsCOMPtr<nsIDOMElement> cell;
- int32_t currentRowIndex, currentColIndex;
- nsCOMPtr<nsIDOMRange> range;
- rv = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- if (rv == NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND) {
- return NS_OK;
- }
- while (cell) {
- rv = GetCellIndexes(cell, ¤tRowIndex, ¤tColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- if (currentRowIndex < maxRow || currentRowIndex > maxRow ||
- currentColIndex < maxColumn || currentColIndex > maxColumn) {
- selection->RemoveRange(range);
- // Since we've removed the range, decrement pointer to next range
- mSelectedCellIndex--;
- }
- rv = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- }
- int32_t rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- for (int32_t row = minRow; row <= maxRow; row++) {
- for (int32_t col = minColumn; col <= maxColumn;
- col += std::max(actualColSpan, 1)) {
- rv = GetCellDataAt(table, row, col, getter_AddRefs(cell),
- ¤tRowIndex, ¤tColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- if (NS_FAILED(rv)) {
- break;
- }
- // Skip cells that already selected or are spanned from previous locations
- if (!isSelected && cell &&
- row == currentRowIndex && col == currentColIndex) {
- rv = AppendNodeToSelectionAsRange(cell);
- if (NS_FAILED(rv)) {
- break;
- }
- }
- }
- }
- // NS_OK, otherwise, the last failure of GetCellDataAt() or
- // AppendNodeToSelectionAsRange().
- return rv;
- }
- NS_IMETHODIMP
- HTMLEditor::SelectAllTableCells()
- {
- nsCOMPtr<nsIDOMElement> cell;
- nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr,
- getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if we didn't find a cell
- NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- nsCOMPtr<nsIDOMElement> startCell = cell;
- // Get parent table
- nsCOMPtr<nsIDOMElement> table;
- rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell,
- getter_AddRefs(table));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!table) {
- return NS_ERROR_NULL_POINTER;
- }
- int32_t rowCount, colCount;
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
- // Suppress nsISelectionListener notification
- // until all selection changes are finished
- SelectionBatcher selectionBatcher(selection);
- // It is now safe to clear the selection
- // BE SURE TO RESET IT BEFORE LEAVING!
- rv = ClearSelection();
- // Select all cells in the same column as current cell
- bool cellSelected = false;
- int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex;
- bool isSelected;
- for (int32_t row = 0; row < rowCount; row++) {
- for (int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) {
- rv = GetCellDataAt(table, row, col, getter_AddRefs(cell),
- ¤tRowIndex, ¤tColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- if (NS_FAILED(rv)) {
- break;
- }
- // Skip cells that are spanned from previous rows or columns
- if (cell && row == currentRowIndex && col == currentColIndex) {
- rv = AppendNodeToSelectionAsRange(cell);
- if (NS_FAILED(rv)) {
- break;
- }
- cellSelected = true;
- }
- }
- }
- // Safety code to select starting cell if nothing else was selected
- if (!cellSelected) {
- return AppendNodeToSelectionAsRange(startCell);
- }
- // NS_OK, otherwise, the error of ClearSelection() when there is no column or
- // the last failure of GetCellDataAt() or AppendNodeToSelectionAsRange().
- return rv;
- }
- NS_IMETHODIMP
- HTMLEditor::SelectTableRow()
- {
- nsCOMPtr<nsIDOMElement> cell;
- nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr,
- getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if we didn't find a cell
- NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- nsCOMPtr<nsIDOMElement> startCell = cell;
- // Get table and location of cell:
- RefPtr<Selection> selection;
- nsCOMPtr<nsIDOMElement> table;
- int32_t startRowIndex, startColIndex;
- rv = GetCellContext(getter_AddRefs(selection),
- getter_AddRefs(table),
- getter_AddRefs(cell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
- int32_t rowCount, colCount;
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- //Note: At this point, we could get first and last cells in row,
- // then call SelectBlockOfCells, but that would take just
- // a little less code, so the following is more efficient
- // Suppress nsISelectionListener notification
- // until all selection changes are finished
- SelectionBatcher selectionBatcher(selection);
- // It is now safe to clear the selection
- // BE SURE TO RESET IT BEFORE LEAVING!
- rv = ClearSelection();
- // Select all cells in the same row as current cell
- bool cellSelected = false;
- int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex;
- bool isSelected;
- for (int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) {
- rv = GetCellDataAt(table, startRowIndex, col, getter_AddRefs(cell),
- ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- if (NS_FAILED(rv)) {
- break;
- }
- // Skip cells that are spanned from previous rows or columns
- if (cell && currentRowIndex == startRowIndex && currentColIndex == col) {
- rv = AppendNodeToSelectionAsRange(cell);
- if (NS_FAILED(rv)) {
- break;
- }
- cellSelected = true;
- }
- }
- // Safety code to select starting cell if nothing else was selected
- if (!cellSelected) {
- return AppendNodeToSelectionAsRange(startCell);
- }
- // NS_OK, otherwise, the error of ClearSelection() when there is no column or
- // the last failure of GetCellDataAt() or AppendNodeToSelectionAsRange().
- return rv;
- }
- NS_IMETHODIMP
- HTMLEditor::SelectTableColumn()
- {
- nsCOMPtr<nsIDOMElement> cell;
- nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr,
- getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if we didn't find a cell
- NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- nsCOMPtr<nsIDOMElement> startCell = cell;
- // Get location of cell:
- RefPtr<Selection> selection;
- nsCOMPtr<nsIDOMElement> table;
- int32_t startRowIndex, startColIndex;
- rv = GetCellContext(getter_AddRefs(selection),
- getter_AddRefs(table),
- getter_AddRefs(cell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
- int32_t rowCount, colCount;
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- // Suppress nsISelectionListener notification
- // until all selection changes are finished
- SelectionBatcher selectionBatcher(selection);
- // It is now safe to clear the selection
- // BE SURE TO RESET IT BEFORE LEAVING!
- rv = ClearSelection();
- // Select all cells in the same column as current cell
- bool cellSelected = false;
- int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex;
- bool isSelected;
- for (int32_t row = 0; row < rowCount; row += std::max(actualRowSpan, 1)) {
- rv = GetCellDataAt(table, row, startColIndex, getter_AddRefs(cell),
- ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- if (NS_FAILED(rv)) {
- break;
- }
- // Skip cells that are spanned from previous rows or columns
- if (cell && currentRowIndex == row && currentColIndex == startColIndex) {
- rv = AppendNodeToSelectionAsRange(cell);
- if (NS_FAILED(rv)) {
- break;
- }
- cellSelected = true;
- }
- }
- // Safety code to select starting cell if nothing else was selected
- if (!cellSelected) {
- return AppendNodeToSelectionAsRange(startCell);
- }
- // NS_OK, otherwise, the error of ClearSelection() when there is no row or
- // the last failure of GetCellDataAt() or AppendNodeToSelectionAsRange().
- return rv;
- }
- NS_IMETHODIMP
- HTMLEditor::SplitTableCell()
- {
- nsCOMPtr<nsIDOMElement> table;
- nsCOMPtr<nsIDOMElement> cell;
- int32_t startRowIndex, startColIndex, actualRowSpan, actualColSpan;
- // Get cell, table, etc. at selection anchor node
- nsresult rv = GetCellContext(nullptr,
- getter_AddRefs(table),
- getter_AddRefs(cell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!table || !cell) {
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- // We need rowspan and colspan data
- rv = GetCellSpansAt(table, startRowIndex, startColIndex,
- actualRowSpan, actualColSpan);
- NS_ENSURE_SUCCESS(rv, rv);
- // Must have some span to split
- if (actualRowSpan <= 1 && actualColSpan <= 1) {
- return NS_OK;
- }
- AutoEditBatch beginBatching(this);
- // Prevent auto insertion of BR in new cell until we're done
- AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
- // We reset selection
- AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
- startColIndex, ePreviousColumn,
- false);
- //...so suppress Rules System selection munging
- AutoTransactionsConserveSelection dontChangeSelection(this);
- nsCOMPtr<nsIDOMElement> newCell;
- int32_t rowIndex = startRowIndex;
- int32_t rowSpanBelow, colSpanAfter;
- // Split up cell row-wise first into rowspan=1 above, and the rest below,
- // whittling away at the cell below until no more extra span
- for (rowSpanBelow = actualRowSpan-1; rowSpanBelow >= 0; rowSpanBelow--) {
- // We really split row-wise only if we had rowspan > 1
- if (rowSpanBelow > 0) {
- rv = SplitCellIntoRows(table, rowIndex, startColIndex, 1, rowSpanBelow,
- getter_AddRefs(newCell));
- NS_ENSURE_SUCCESS(rv, rv);
- CopyCellBackgroundColor(newCell, cell);
- }
- int32_t colIndex = startColIndex;
- // Now split the cell with rowspan = 1 into cells if it has colSpan > 1
- for (colSpanAfter = actualColSpan-1; colSpanAfter > 0; colSpanAfter--) {
- rv = SplitCellIntoColumns(table, rowIndex, colIndex, 1, colSpanAfter,
- getter_AddRefs(newCell));
- NS_ENSURE_SUCCESS(rv, rv);
- CopyCellBackgroundColor(newCell, cell);
- colIndex++;
- }
- // Point to the new cell and repeat
- rowIndex++;
- }
- return NS_OK;
- }
- nsresult
- HTMLEditor::CopyCellBackgroundColor(nsIDOMElement* destCell,
- nsIDOMElement* sourceCell)
- {
- NS_ENSURE_TRUE(destCell && sourceCell, NS_ERROR_NULL_POINTER);
- // Copy backgournd color to new cell
- NS_NAMED_LITERAL_STRING(bgcolor, "bgcolor");
- nsAutoString color;
- bool isSet;
- nsresult rv = GetAttributeValue(sourceCell, bgcolor, color, &isSet);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!isSet) {
- return NS_OK;
- }
- return SetAttribute(destCell, bgcolor, color);
- }
- NS_IMETHODIMP
- HTMLEditor::SplitCellIntoColumns(nsIDOMElement* aTable,
- int32_t aRowIndex,
- int32_t aColIndex,
- int32_t aColSpanLeft,
- int32_t aColSpanRight,
- nsIDOMElement** aNewCell)
- {
- NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
- if (aNewCell) {
- *aNewCell = nullptr;
- }
- nsCOMPtr<nsIDOMElement> cell;
- int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- nsresult rv =
- GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell),
- &startRowIndex, &startColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER);
- // We can't split!
- if (actualColSpan <= 1 || (aColSpanLeft + aColSpanRight) > actualColSpan) {
- return NS_OK;
- }
- // Reduce colspan of cell to split
- rv = SetColSpan(cell, aColSpanLeft);
- NS_ENSURE_SUCCESS(rv, rv);
- // Insert new cell after using the remaining span
- // and always get the new cell so we can copy the background color;
- nsCOMPtr<nsIDOMElement> newCell;
- rv = InsertCell(cell, actualRowSpan, aColSpanRight, true, false,
- getter_AddRefs(newCell));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!newCell) {
- return NS_OK;
- }
- if (aNewCell) {
- NS_ADDREF(*aNewCell = newCell.get());
- }
- return CopyCellBackgroundColor(newCell, cell);
- }
- NS_IMETHODIMP
- HTMLEditor::SplitCellIntoRows(nsIDOMElement* aTable,
- int32_t aRowIndex,
- int32_t aColIndex,
- int32_t aRowSpanAbove,
- int32_t aRowSpanBelow,
- nsIDOMElement** aNewCell)
- {
- NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
- if (aNewCell) *aNewCell = nullptr;
- nsCOMPtr<nsIDOMElement> cell;
- int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- nsresult rv =
- GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell),
- &startRowIndex, &startColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER);
- // We can't split!
- if (actualRowSpan <= 1 || (aRowSpanAbove + aRowSpanBelow) > actualRowSpan) {
- return NS_OK;
- }
- int32_t rowCount, colCount;
- rv = GetTableSize(aTable, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIDOMElement> cell2;
- nsCOMPtr<nsIDOMElement> lastCellFound;
- int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2;
- bool isSelected2;
- int32_t colIndex = 0;
- bool insertAfter = (startColIndex > 0);
- // This is the row we will insert new cell into
- int32_t rowBelowIndex = startRowIndex+aRowSpanAbove;
- // Find a cell to insert before or after
- for (;;) {
- // Search for a cell to insert before
- rv = GetCellDataAt(aTable, rowBelowIndex,
- colIndex, getter_AddRefs(cell2),
- &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2,
- &actualRowSpan2, &actualColSpan2, &isSelected2);
- // If we fail here, it could be because row has bad rowspan values,
- // such as all cells having rowspan > 1 (Call FixRowSpan first!)
- if (NS_FAILED(rv) || !cell) {
- return NS_ERROR_FAILURE;
- }
- // Skip over cells spanned from above (like the one we are splitting!)
- if (cell2 && startRowIndex2 == rowBelowIndex) {
- if (!insertAfter) {
- // Inserting before, so stop at first cell in row we want to insert
- // into.
- break;
- }
- // New cell isn't first in row,
- // so stop after we find the cell just before new cell's column
- if (startColIndex2 + actualColSpan2 == startColIndex) {
- break;
- }
- // If cell found is AFTER desired new cell colum,
- // we have multiple cells with rowspan > 1 that
- // prevented us from finding a cell to insert after...
- if (startColIndex2 > startColIndex) {
- // ... so instead insert before the cell we found
- insertAfter = false;
- break;
- }
- lastCellFound = cell2;
- }
- // Skip to next available cellmap location
- colIndex += std::max(actualColSpan2, 1);
- // Done when past end of total number of columns
- if (colIndex > colCount) {
- break;
- }
- }
- if (!cell2 && lastCellFound) {
- // Edge case where we didn't find a cell to insert after
- // or before because column(s) before desired column
- // and all columns after it are spanned from above.
- // We can insert after the last cell we found
- cell2 = lastCellFound;
- insertAfter = true; // Should always be true, but let's be sure
- }
- // Reduce rowspan of cell to split
- rv = SetRowSpan(cell, aRowSpanAbove);
- NS_ENSURE_SUCCESS(rv, rv);
- // Insert new cell after using the remaining span
- // and always get the new cell so we can copy the background color;
- nsCOMPtr<nsIDOMElement> newCell;
- rv = InsertCell(cell2, aRowSpanBelow, actualColSpan, insertAfter, false,
- getter_AddRefs(newCell));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!newCell) {
- return NS_OK;
- }
- if (aNewCell) {
- NS_ADDREF(*aNewCell = newCell.get());
- }
- return CopyCellBackgroundColor(newCell, cell2);
- }
- NS_IMETHODIMP
- HTMLEditor::SwitchTableCellHeaderType(nsIDOMElement* aSourceCell,
- nsIDOMElement** aNewCell)
- {
- nsCOMPtr<Element> sourceCell = do_QueryInterface(aSourceCell);
- NS_ENSURE_TRUE(sourceCell, NS_ERROR_NULL_POINTER);
- AutoEditBatch beginBatching(this);
- // Prevent auto insertion of BR in new cell created by ReplaceContainer
- AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
- // Save current selection to restore when done
- // This is needed so ReplaceContainer can monitor selection
- // when replacing nodes
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
- AutoSelectionRestorer selectionRestorer(selection, this);
- // Set to the opposite of current type
- nsCOMPtr<nsIAtom> atom = EditorBase::GetTag(aSourceCell);
- nsIAtom* newCellType = atom == nsGkAtoms::td ? nsGkAtoms::th : nsGkAtoms::td;
- // This creates new node, moves children, copies attributes (true)
- // and manages the selection!
- nsCOMPtr<Element> newNode = ReplaceContainer(sourceCell, newCellType,
- nullptr, nullptr, EditorBase::eCloneAttributes);
- NS_ENSURE_TRUE(newNode, NS_ERROR_FAILURE);
- // Return the new cell
- if (aNewCell) {
- nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newNode);
- *aNewCell = newElement.get();
- NS_ADDREF(*aNewCell);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents)
- {
- nsCOMPtr<nsIDOMElement> table;
- nsCOMPtr<nsIDOMElement> targetCell;
- int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- nsCOMPtr<nsIDOMElement> cell2;
- int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2;
- bool isSelected2;
- // Get cell, table, etc. at selection anchor node
- nsresult rv = GetCellContext(nullptr,
- getter_AddRefs(table),
- getter_AddRefs(targetCell),
- nullptr, nullptr,
- &startRowIndex, &startColIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!table || !targetCell) {
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- AutoEditBatch beginBatching(this);
- //Don't let Rules System change the selection
- AutoTransactionsConserveSelection dontChangeSelection(this);
- // Note: We dont' use AutoSelectionSetterAfterTableEdit here so the selection
- // is retained after joining. This leaves the target cell selected
- // as well as the "non-contiguous" cells, so user can see what happened.
- nsCOMPtr<nsIDOMElement> firstCell;
- int32_t firstRowIndex, firstColIndex;
- rv = GetFirstSelectedCellInTable(&firstRowIndex, &firstColIndex,
- getter_AddRefs(firstCell));
- NS_ENSURE_SUCCESS(rv, rv);
- bool joinSelectedCells = false;
- if (firstCell) {
- nsCOMPtr<nsIDOMElement> secondCell;
- rv = GetNextSelectedCell(nullptr, getter_AddRefs(secondCell));
- NS_ENSURE_SUCCESS(rv, rv);
- // If only one cell is selected, join with cell to the right
- joinSelectedCells = (secondCell != nullptr);
- }
- if (joinSelectedCells) {
- // We have selected cells: Join just contiguous cells
- // and just merge contents if not contiguous
- int32_t rowCount, colCount;
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- // Get spans for cell we will merge into
- int32_t firstRowSpan, firstColSpan;
- rv = GetCellSpansAt(table, firstRowIndex, firstColIndex,
- firstRowSpan, firstColSpan);
- NS_ENSURE_SUCCESS(rv, rv);
- // This defines the last indexes along the "edges"
- // of the contiguous block of cells, telling us
- // that we can join adjacent cells to the block
- // Start with same as the first values,
- // then expand as we find adjacent selected cells
- int32_t lastRowIndex = firstRowIndex;
- int32_t lastColIndex = firstColIndex;
- int32_t rowIndex, colIndex;
- // First pass: Determine boundaries of contiguous rectangular block
- // that we will join into one cell,
- // favoring adjacent cells in the same row
- for (rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++) {
- int32_t currentRowCount = rowCount;
- // Be sure each row doesn't have rowspan errors
- rv = FixBadRowSpan(table, rowIndex, rowCount);
- NS_ENSURE_SUCCESS(rv, rv);
- // Adjust rowcount by number of rows we removed
- lastRowIndex -= (currentRowCount-rowCount);
- bool cellFoundInRow = false;
- bool lastRowIsSet = false;
- int32_t lastColInRow = 0;
- int32_t firstColInRow = firstColIndex;
- for (colIndex = firstColIndex; colIndex < colCount;
- colIndex += std::max(actualColSpan2, 1)) {
- rv = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2),
- &startRowIndex2, &startColIndex2,
- &rowSpan2, &colSpan2,
- &actualRowSpan2, &actualColSpan2, &isSelected2);
- NS_ENSURE_SUCCESS(rv, rv);
- if (isSelected2) {
- if (!cellFoundInRow) {
- // We've just found the first selected cell in this row
- firstColInRow = colIndex;
- }
- if (rowIndex > firstRowIndex && firstColInRow != firstColIndex) {
- // We're in at least the second row,
- // but left boundary is "ragged" (not the same as 1st row's start)
- //Let's just end block on previous row
- // and keep previous lastColIndex
- //TODO: We could try to find the Maximum firstColInRow
- // so our block can still extend down more rows?
- lastRowIndex = std::max(0,rowIndex - 1);
- lastRowIsSet = true;
- break;
- }
- // Save max selected column in this row, including extra colspan
- lastColInRow = colIndex + (actualColSpan2-1);
- cellFoundInRow = true;
- } else if (cellFoundInRow) {
- // No cell or not selected, but at least one cell in row was found
- if (rowIndex > (firstRowIndex + 1) && colIndex <= lastColIndex) {
- // Cell is in a column less than current right border in
- // the third or higher selected row, so stop block at the previous row
- lastRowIndex = std::max(0,rowIndex - 1);
- lastRowIsSet = true;
- }
- // We're done with this row
- break;
- }
- } // End of column loop
- // Done with this row
- if (cellFoundInRow) {
- if (rowIndex == firstRowIndex) {
- // First row always initializes the right boundary
- lastColIndex = lastColInRow;
- }
- // If we didn't determine last row above...
- if (!lastRowIsSet) {
- if (colIndex < lastColIndex) {
- // (don't think we ever get here?)
- // Cell is in a column less than current right boundary,
- // so stop block at the previous row
- lastRowIndex = std::max(0,rowIndex - 1);
- } else {
- // Go on to examine next row
- lastRowIndex = rowIndex+1;
- }
- }
- // Use the minimum col we found so far for right boundary
- lastColIndex = std::min(lastColIndex, lastColInRow);
- } else {
- // No selected cells in this row -- stop at row above
- // and leave last column at its previous value
- lastRowIndex = std::max(0,rowIndex - 1);
- }
- }
- // The list of cells we will delete after joining
- nsTArray<nsCOMPtr<nsIDOMElement> > deleteList;
- // 2nd pass: Do the joining and merging
- for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
- for (colIndex = 0; colIndex < colCount;
- colIndex += std::max(actualColSpan2, 1)) {
- rv = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2),
- &startRowIndex2, &startColIndex2,
- &rowSpan2, &colSpan2,
- &actualRowSpan2, &actualColSpan2, &isSelected2);
- NS_ENSURE_SUCCESS(rv, rv);
- // If this is 0, we are past last cell in row, so exit the loop
- if (!actualColSpan2) {
- break;
- }
- // Merge only selected cells (skip cell we're merging into, of course)
- if (isSelected2 && cell2 != firstCell) {
- if (rowIndex >= firstRowIndex && rowIndex <= lastRowIndex &&
- colIndex >= firstColIndex && colIndex <= lastColIndex) {
- // We are within the join region
- // Problem: It is very tricky to delete cells as we merge,
- // since that will upset the cellmap
- // Instead, build a list of cells to delete and do it later
- NS_ASSERTION(startRowIndex2 == rowIndex, "JoinTableCells: StartRowIndex is in row above");
- if (actualColSpan2 > 1) {
- //Check if cell "hangs" off the boundary because of colspan > 1
- // Use split methods to chop off excess
- int32_t extraColSpan = (startColIndex2 + actualColSpan2) - (lastColIndex+1);
- if ( extraColSpan > 0) {
- rv = SplitCellIntoColumns(table, startRowIndex2, startColIndex2,
- actualColSpan2 - extraColSpan,
- extraColSpan, nullptr);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- rv = MergeCells(firstCell, cell2, false);
- NS_ENSURE_SUCCESS(rv, rv);
- // Add cell to list to delete
- deleteList.AppendElement(cell2.get());
- } else if (aMergeNonContiguousContents) {
- // Cell is outside join region -- just merge the contents
- rv = MergeCells(firstCell, cell2, false);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- }
- }
- // All cell contents are merged. Delete the empty cells we accumulated
- // Prevent rules testing until we're done
- AutoRules beginRulesSniffing(this, EditAction::deleteNode,
- nsIEditor::eNext);
- for (uint32_t i = 0, n = deleteList.Length(); i < n; i++) {
- nsIDOMElement *elementPtr = deleteList[i];
- if (elementPtr) {
- nsCOMPtr<nsIDOMNode> node = do_QueryInterface(elementPtr);
- rv = DeleteNode(node);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- // Cleanup selection: remove ranges where cells were deleted
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
- int32_t rangeCount;
- rv = selection->GetRangeCount(&rangeCount);
- NS_ENSURE_SUCCESS(rv, rv);
- RefPtr<nsRange> range;
- for (int32_t i = 0; i < rangeCount; i++) {
- range = selection->GetRangeAt(i);
- NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
- nsCOMPtr<nsIDOMElement> deletedCell;
- GetCellFromRange(range, getter_AddRefs(deletedCell));
- if (!deletedCell) {
- selection->RemoveRange(range);
- rangeCount--;
- i--;
- }
- }
- // Set spans for the cell everthing merged into
- rv = SetRowSpan(firstCell, lastRowIndex-firstRowIndex+1);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = SetColSpan(firstCell, lastColIndex-firstColIndex+1);
- NS_ENSURE_SUCCESS(rv, rv);
- // Fixup disturbances in table layout
- NormalizeTable(table);
- } else {
- // Joining with cell to the right -- get rowspan and colspan data of target cell
- rv = GetCellDataAt(table, startRowIndex, startColIndex,
- getter_AddRefs(targetCell),
- &startRowIndex, &startColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(targetCell, NS_ERROR_NULL_POINTER);
- // Get data for cell to the right
- rv = GetCellDataAt(table, startRowIndex, startColIndex + actualColSpan,
- getter_AddRefs(cell2),
- &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2,
- &actualRowSpan2, &actualColSpan2, &isSelected2);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!cell2) {
- return NS_OK; // Don't fail if there's no cell
- }
- // sanity check
- NS_ASSERTION((startRowIndex >= startRowIndex2),"JoinCells: startRowIndex < startRowIndex2");
- // Figure out span of merged cell starting from target's starting row
- // to handle case of merged cell starting in a row above
- int32_t spanAboveMergedCell = startRowIndex - startRowIndex2;
- int32_t effectiveRowSpan2 = actualRowSpan2 - spanAboveMergedCell;
- if (effectiveRowSpan2 > actualRowSpan) {
- // Cell to the right spans into row below target
- // Split off portion below target cell's bottom-most row
- rv = SplitCellIntoRows(table, startRowIndex2, startColIndex2,
- spanAboveMergedCell+actualRowSpan,
- effectiveRowSpan2-actualRowSpan, nullptr);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Move contents from cell to the right
- // Delete the cell now only if it starts in the same row
- // and has enough row "height"
- rv = MergeCells(targetCell, cell2,
- (startRowIndex2 == startRowIndex) &&
- (effectiveRowSpan2 >= actualRowSpan));
- NS_ENSURE_SUCCESS(rv, rv);
- if (effectiveRowSpan2 < actualRowSpan) {
- // Merged cell is "shorter"
- // (there are cells(s) below it that are row-spanned by target cell)
- // We could try splitting those cells, but that's REAL messy,
- // so the safest thing to do is NOT really join the cells
- return NS_OK;
- }
- if (spanAboveMergedCell > 0) {
- // Cell we merged started in a row above the target cell
- // Reduce rowspan to give room where target cell will extend its colspan
- rv = SetRowSpan(cell2, spanAboveMergedCell);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Reset target cell's colspan to encompass cell to the right
- rv = SetColSpan(targetCell, actualColSpan+actualColSpan2);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::MergeCells(nsCOMPtr<nsIDOMElement> aTargetCell,
- nsCOMPtr<nsIDOMElement> aCellToMerge,
- bool aDeleteCellToMerge)
- {
- nsCOMPtr<dom::Element> targetCell = do_QueryInterface(aTargetCell);
- nsCOMPtr<dom::Element> cellToMerge = do_QueryInterface(aCellToMerge);
- NS_ENSURE_TRUE(targetCell && cellToMerge, NS_ERROR_NULL_POINTER);
- // Prevent rules testing until we're done
- AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
- // Don't need to merge if cell is empty
- if (!IsEmptyCell(cellToMerge)) {
- // Get index of last child in target cell
- // If we fail or don't have children,
- // we insert at index 0
- int32_t insertIndex = 0;
- // Start inserting just after last child
- uint32_t len = targetCell->GetChildCount();
- if (len == 1 && IsEmptyCell(targetCell)) {
- // Delete the empty node
- nsIContent* cellChild = targetCell->GetFirstChild();
- nsresult rv = DeleteNode(cellChild->AsDOMNode());
- NS_ENSURE_SUCCESS(rv, rv);
- insertIndex = 0;
- } else {
- insertIndex = (int32_t)len;
- }
- // Move the contents
- while (cellToMerge->HasChildren()) {
- nsCOMPtr<nsIDOMNode> cellChild = cellToMerge->GetLastChild()->AsDOMNode();
- nsresult rv = DeleteNode(cellChild);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = InsertNode(cellChild, aTargetCell, insertIndex);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- // Delete cells whose contents were moved
- if (aDeleteCellToMerge) {
- return DeleteNode(aCellToMerge);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::FixBadRowSpan(nsIDOMElement* aTable,
- int32_t aRowIndex,
- int32_t& aNewRowCount)
- {
- NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
- int32_t rowCount, colCount;
- nsresult rv = GetTableSize(aTable, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIDOMElement>cell;
- int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- int32_t minRowSpan = -1;
- int32_t colIndex;
- for (colIndex = 0; colIndex < colCount;
- colIndex += std::max(actualColSpan, 1)) {
- rv = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell),
- &startRowIndex, &startColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- // NOTE: This is a *real* failure.
- // GetCellDataAt passes if cell is missing from cellmap
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!cell) {
- break;
- }
- if (rowSpan > 0 &&
- startRowIndex == aRowIndex &&
- (rowSpan < minRowSpan || minRowSpan == -1)) {
- minRowSpan = rowSpan;
- }
- NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan");
- }
- if (minRowSpan > 1) {
- // The amount to reduce everyone's rowspan
- // so at least one cell has rowspan = 1
- int32_t rowsReduced = minRowSpan - 1;
- for (colIndex = 0; colIndex < colCount;
- colIndex += std::max(actualColSpan, 1)) {
- rv = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell),
- &startRowIndex, &startColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- if (NS_FAILED(rv)) {
- return rv;
- }
- // Fixup rowspans only for cells starting in current row
- if (cell && rowSpan > 0 &&
- startRowIndex == aRowIndex &&
- startColIndex == colIndex ) {
- rv = SetRowSpan(cell, rowSpan-rowsReduced);
- if (NS_FAILED(rv)) {
- return rv;
- }
- }
- NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan");
- }
- }
- return GetTableSize(aTable, &aNewRowCount, &colCount);
- }
- NS_IMETHODIMP
- HTMLEditor::FixBadColSpan(nsIDOMElement* aTable,
- int32_t aColIndex,
- int32_t& aNewColCount)
- {
- NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
- int32_t rowCount, colCount;
- nsresult rv = GetTableSize(aTable, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIDOMElement> cell;
- int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- int32_t minColSpan = -1;
- int32_t rowIndex;
- for (rowIndex = 0; rowIndex < rowCount;
- rowIndex += std::max(actualRowSpan, 1)) {
- rv = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell),
- &startRowIndex, &startColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- // NOTE: This is a *real* failure.
- // GetCellDataAt passes if cell is missing from cellmap
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!cell) {
- break;
- }
- if (colSpan > 0 &&
- startColIndex == aColIndex &&
- (colSpan < minColSpan || minColSpan == -1)) {
- minColSpan = colSpan;
- }
- NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan");
- }
- if (minColSpan > 1) {
- // The amount to reduce everyone's colspan
- // so at least one cell has colspan = 1
- int32_t colsReduced = minColSpan - 1;
- for (rowIndex = 0; rowIndex < rowCount;
- rowIndex += std::max(actualRowSpan, 1)) {
- rv = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell),
- &startRowIndex, &startColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- if (NS_FAILED(rv)) {
- return rv;
- }
- // Fixup colspans only for cells starting in current column
- if (cell && colSpan > 0 &&
- startColIndex == aColIndex &&
- startRowIndex == rowIndex) {
- rv = SetColSpan(cell, colSpan-colsReduced);
- if (NS_FAILED(rv)) {
- return rv;
- }
- }
- NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan");
- }
- }
- return GetTableSize(aTable, &rowCount, &aNewColCount);
- }
- NS_IMETHODIMP
- HTMLEditor::NormalizeTable(nsIDOMElement* aTable)
- {
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
- nsCOMPtr<nsIDOMElement> table;
- nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"),
- aTable, getter_AddRefs(table));
- NS_ENSURE_SUCCESS(rv, rv);
- // Don't fail if we didn't find a table
- NS_ENSURE_TRUE(table, NS_OK);
- int32_t rowCount, colCount, rowIndex, colIndex;
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- // Save current selection
- AutoSelectionRestorer selectionRestorer(selection, this);
- AutoEditBatch beginBatching(this);
- // Prevent auto insertion of BR in new cell until we're done
- AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
- nsCOMPtr<nsIDOMElement> cell;
- int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- // Scan all cells in each row to detect bad rowspan values
- for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
- rv = FixBadRowSpan(table, rowIndex, rowCount);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // and same for colspans
- for (colIndex = 0; colIndex < colCount; colIndex++) {
- rv = FixBadColSpan(table, colIndex, colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Fill in missing cellmap locations with empty cells
- for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
- nsCOMPtr<nsIDOMElement> previousCellInRow;
- for (colIndex = 0; colIndex < colCount; colIndex++) {
- rv = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell),
- &startRowIndex, &startColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- // NOTE: This is a *real* failure.
- // GetCellDataAt passes if cell is missing from cellmap
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!cell) {
- //We are missing a cell at a cellmap location
- #ifdef DEBUG
- printf("NormalizeTable found missing cell at row=%d, col=%d\n",
- rowIndex, colIndex);
- #endif
- // Add a cell after the previous Cell in the current row
- if (!previousCellInRow) {
- // We don't have any cells in this row -- We are really messed up!
- #ifdef DEBUG
- printf("NormalizeTable found no cells in row=%d, col=%d\n",
- rowIndex, colIndex);
- #endif
- return NS_ERROR_FAILURE;
- }
- // Insert a new cell after (true), and return the new cell to us
- rv = InsertCell(previousCellInRow, 1, 1, true, false,
- getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- // Set this so we use returned new "cell" to set previousCellInRow below
- if (cell) {
- startRowIndex = rowIndex;
- }
- }
- // Save the last cell found in the same row we are scanning
- if (startRowIndex == rowIndex) {
- previousCellInRow = cell;
- }
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::GetCellIndexes(nsIDOMElement* aCell,
- int32_t* aRowIndex,
- int32_t* aColIndex)
- {
- NS_ENSURE_ARG_POINTER(aRowIndex);
- *aColIndex=0; // initialize out params
- NS_ENSURE_ARG_POINTER(aColIndex);
- *aRowIndex=0;
- if (!aCell) {
- // Get the selected cell or the cell enclosing the selection anchor
- nsCOMPtr<nsIDOMElement> cell;
- nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr,
- getter_AddRefs(cell));
- if (NS_FAILED(rv) || !cell) {
- return NS_ERROR_FAILURE;
- }
- aCell = cell;
- }
- nsCOMPtr<nsIPresShell> ps = GetPresShell();
- NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
- nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aCell) );
- NS_ENSURE_TRUE(nodeAsContent, NS_ERROR_FAILURE);
- // frames are not ref counted, so don't use an nsCOMPtr
- nsIFrame *layoutObject = nodeAsContent->GetPrimaryFrame();
- NS_ENSURE_TRUE(layoutObject, NS_ERROR_FAILURE);
- nsITableCellLayout *cellLayoutObject = do_QueryFrame(layoutObject);
- NS_ENSURE_TRUE(cellLayoutObject, NS_ERROR_FAILURE);
- return cellLayoutObject->GetCellIndexes(*aRowIndex, *aColIndex);
- }
- nsTableWrapperFrame*
- HTMLEditor::GetTableFrame(nsIDOMElement* aTable)
- {
- NS_ENSURE_TRUE(aTable, nullptr);
- nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aTable) );
- NS_ENSURE_TRUE(nodeAsContent, nullptr);
- return do_QueryFrame(nodeAsContent->GetPrimaryFrame());
- }
- //Return actual number of cells (a cell with colspan > 1 counts as just 1)
- int32_t
- HTMLEditor::GetNumberOfCellsInRow(nsIDOMElement* aTable,
- int32_t rowIndex)
- {
- int32_t cellCount = 0;
- nsCOMPtr<nsIDOMElement> cell;
- int32_t colIndex = 0;
- do {
- int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- nsresult rv =
- GetCellDataAt(aTable, rowIndex, colIndex, getter_AddRefs(cell),
- &startRowIndex, &startColIndex, &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, 0);
- if (cell) {
- // Only count cells that start in row we are working with
- if (startRowIndex == rowIndex) {
- cellCount++;
- }
- //Next possible location for a cell
- colIndex += actualColSpan;
- } else {
- colIndex++;
- }
- } while (cell);
- return cellCount;
- }
- NS_IMETHODIMP
- HTMLEditor::GetTableSize(nsIDOMElement* aTable,
- int32_t* aRowCount,
- int32_t* aColCount)
- {
- NS_ENSURE_ARG_POINTER(aRowCount);
- NS_ENSURE_ARG_POINTER(aColCount);
- *aRowCount = 0;
- *aColCount = 0;
- nsCOMPtr<nsIDOMElement> table;
- // Get the selected talbe or the table enclosing the selection anchor
- nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTable,
- getter_AddRefs(table));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
- nsTableWrapperFrame* tableFrame = GetTableFrame(table.get());
- NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE);
- *aRowCount = tableFrame->GetRowCount();
- *aColCount = tableFrame->GetColCount();
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::GetCellDataAt(nsIDOMElement* aTable,
- int32_t aRowIndex,
- int32_t aColIndex,
- nsIDOMElement** aCell,
- int32_t* aStartRowIndex,
- int32_t* aStartColIndex,
- int32_t* aRowSpan,
- int32_t* aColSpan,
- int32_t* aActualRowSpan,
- int32_t* aActualColSpan,
- bool* aIsSelected)
- {
- NS_ENSURE_ARG_POINTER(aStartRowIndex);
- NS_ENSURE_ARG_POINTER(aStartColIndex);
- NS_ENSURE_ARG_POINTER(aRowSpan);
- NS_ENSURE_ARG_POINTER(aColSpan);
- NS_ENSURE_ARG_POINTER(aActualRowSpan);
- NS_ENSURE_ARG_POINTER(aActualColSpan);
- NS_ENSURE_ARG_POINTER(aIsSelected);
- NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
- *aStartRowIndex = 0;
- *aStartColIndex = 0;
- *aRowSpan = 0;
- *aColSpan = 0;
- *aActualRowSpan = 0;
- *aActualColSpan = 0;
- *aIsSelected = false;
- *aCell = nullptr;
- if (!aTable) {
- // Get the selected table or the table enclosing the selection anchor
- nsCOMPtr<nsIDOMElement> table;
- nsresult rv =
- GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr,
- getter_AddRefs(table));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!table) {
- return NS_ERROR_FAILURE;
- }
- aTable = table;
- }
- nsTableWrapperFrame* tableFrame = GetTableFrame(aTable);
- NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE);
- nsTableCellFrame* cellFrame =
- tableFrame->GetCellFrameAt(aRowIndex, aColIndex);
- if (!cellFrame) {
- return NS_ERROR_FAILURE;
- }
- *aIsSelected = cellFrame->IsSelected();
- *aStartRowIndex = cellFrame->RowIndex();
- *aStartColIndex = cellFrame->ColIndex();
- *aRowSpan = cellFrame->GetRowSpan();
- *aColSpan = cellFrame->GetColSpan();
- *aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex);
- *aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex);
- nsCOMPtr<nsIDOMElement> domCell = do_QueryInterface(cellFrame->GetContent());
- domCell.forget(aCell);
- return NS_OK;
- }
- // When all you want is the cell
- NS_IMETHODIMP
- HTMLEditor::GetCellAt(nsIDOMElement* aTable,
- int32_t aRowIndex,
- int32_t aColIndex,
- nsIDOMElement** aCell)
- {
- NS_ENSURE_ARG_POINTER(aCell);
- *aCell = nullptr;
- if (!aTable) {
- // Get the selected table or the table enclosing the selection anchor
- nsCOMPtr<nsIDOMElement> table;
- nsresult rv =
- GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr,
- getter_AddRefs(table));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
- aTable = table;
- }
- nsTableWrapperFrame* tableFrame = GetTableFrame(aTable);
- if (!tableFrame) {
- *aCell = nullptr;
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- nsCOMPtr<nsIDOMElement> domCell =
- do_QueryInterface(tableFrame->GetCellAt(aRowIndex, aColIndex));
- domCell.forget(aCell);
- return NS_OK;
- }
- // When all you want are the rowspan and colspan (not exposed in nsITableEditor)
- NS_IMETHODIMP
- HTMLEditor::GetCellSpansAt(nsIDOMElement* aTable,
- int32_t aRowIndex,
- int32_t aColIndex,
- int32_t& aActualRowSpan,
- int32_t& aActualColSpan)
- {
- nsTableWrapperFrame* tableFrame = GetTableFrame(aTable);
- if (!tableFrame) {
- return NS_ERROR_FAILURE;
- }
- aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex);
- aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex);
- return NS_OK;
- }
- nsresult
- HTMLEditor::GetCellContext(Selection** aSelection,
- nsIDOMElement** aTable,
- nsIDOMElement** aCell,
- nsIDOMNode** aCellParent,
- int32_t* aCellOffset,
- int32_t* aRowIndex,
- int32_t* aColIndex)
- {
- // Initialize return pointers
- if (aSelection) {
- *aSelection = nullptr;
- }
- if (aTable) {
- *aTable = nullptr;
- }
- if (aCell) {
- *aCell = nullptr;
- }
- if (aCellParent) {
- *aCellParent = nullptr;
- }
- if (aCellOffset) {
- *aCellOffset = 0;
- }
- if (aRowIndex) {
- *aRowIndex = 0;
- }
- if (aColIndex) {
- *aColIndex = 0;
- }
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
- if (aSelection) {
- *aSelection = selection.get();
- NS_ADDREF(*aSelection);
- }
- nsCOMPtr <nsIDOMElement> table;
- nsCOMPtr <nsIDOMElement> cell;
- // Caller may supply the cell...
- if (aCell && *aCell) {
- cell = *aCell;
- }
- // ...but if not supplied,
- // get cell if it's the child of selection anchor node,
- // or get the enclosing by a cell
- if (!cell) {
- // Find a selected or enclosing table element
- nsCOMPtr<nsIDOMElement> cellOrTableElement;
- int32_t selectedCount;
- nsAutoString tagName;
- nsresult rv =
- GetSelectedOrParentTableElement(tagName, &selectedCount,
- getter_AddRefs(cellOrTableElement));
- NS_ENSURE_SUCCESS(rv, rv);
- if (tagName.EqualsLiteral("table")) {
- // We have a selected table, not a cell
- if (aTable) {
- *aTable = cellOrTableElement.get();
- NS_ADDREF(*aTable);
- }
- return NS_OK;
- }
- if (!tagName.EqualsLiteral("td")) {
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- // We found a cell
- cell = cellOrTableElement;
- }
- if (aCell) {
- *aCell = cell.get();
- NS_ADDREF(*aCell);
- }
- // Get containing table
- nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell,
- getter_AddRefs(table));
- NS_ENSURE_SUCCESS(rv, rv);
- // Cell must be in a table, so fail if not found
- NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
- if (aTable) {
- *aTable = table.get();
- NS_ADDREF(*aTable);
- }
- // Get the rest of the related data only if requested
- if (aRowIndex || aColIndex) {
- int32_t rowIndex, colIndex;
- // Get current cell location so we can put caret back there when done
- rv = GetCellIndexes(cell, &rowIndex, &colIndex);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (aRowIndex) {
- *aRowIndex = rowIndex;
- }
- if (aColIndex) {
- *aColIndex = colIndex;
- }
- }
- if (aCellParent) {
- nsCOMPtr <nsIDOMNode> cellParent;
- // Get the immediate parent of the cell
- rv = cell->GetParentNode(getter_AddRefs(cellParent));
- NS_ENSURE_SUCCESS(rv, rv);
- // Cell has to have a parent, so fail if not found
- NS_ENSURE_TRUE(cellParent, NS_ERROR_FAILURE);
- *aCellParent = cellParent.get();
- NS_ADDREF(*aCellParent);
- if (aCellOffset) {
- *aCellOffset = GetChildOffset(cell, cellParent);
- }
- }
- return NS_OK;
- }
- nsresult
- HTMLEditor::GetCellFromRange(nsRange* aRange,
- nsIDOMElement** aCell)
- {
- // Note: this might return a node that is outside of the range.
- // Use carefully.
- NS_ENSURE_TRUE(aRange && aCell, NS_ERROR_NULL_POINTER);
- *aCell = nullptr;
- nsCOMPtr<nsIDOMNode> startParent;
- nsresult rv = aRange->GetStartContainer(getter_AddRefs(startParent));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE);
- uint32_t startOffset = aRange->StartOffset();
- nsCOMPtr<nsIDOMNode> childNode =
- GetChildAt(startParent, static_cast<int32_t>(startOffset));
- // This means selection is probably at a text node (or end of doc?)
- if (!childNode) {
- return NS_ERROR_FAILURE;
- }
- nsCOMPtr<nsIDOMNode> endParent;
- rv = aRange->GetEndContainer(getter_AddRefs(endParent));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE);
- // If a cell is deleted, the range is collapse
- // (startOffset == aRange->EndOffset())
- // so tell caller the cell wasn't found
- if (startParent == endParent &&
- aRange->EndOffset() == startOffset+1 &&
- HTMLEditUtils::IsTableCell(childNode)) {
- // Should we also test if frame is selected? (Use GetCellDataAt())
- // (Let's not for now -- more efficient)
- nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childNode);
- *aCell = cellElement.get();
- NS_ADDREF(*aCell);
- return NS_OK;
- }
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- NS_IMETHODIMP
- HTMLEditor::GetFirstSelectedCell(nsIDOMRange** aRange,
- nsIDOMElement** aCell)
- {
- NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
- *aCell = nullptr;
- if (aRange) {
- *aRange = nullptr;
- }
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
- RefPtr<nsRange> range = selection->GetRangeAt(0);
- NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
- mSelectedCellIndex = 0;
- nsresult rv = GetCellFromRange(range, aCell);
- // Failure here probably means selection is in a text node,
- // so there's no selected cell
- if (NS_FAILED(rv)) {
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- // No cell means range was collapsed (cell was deleted)
- if (!*aCell) {
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- if (aRange) {
- *aRange = range.get();
- NS_ADDREF(*aRange);
- }
- // Setup for next cell
- mSelectedCellIndex = 1;
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::GetNextSelectedCell(nsIDOMRange** aRange,
- nsIDOMElement** aCell)
- {
- NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
- *aCell = nullptr;
- if (aRange) {
- *aRange = nullptr;
- }
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
- int32_t rangeCount = selection->RangeCount();
- // Don't even try if index exceeds range count
- if (mSelectedCellIndex >= rangeCount) {
- return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
- }
- // Scan through ranges to find next valid selected cell
- RefPtr<nsRange> range;
- for (; mSelectedCellIndex < rangeCount; mSelectedCellIndex++) {
- range = selection->GetRangeAt(mSelectedCellIndex);
- NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
- nsresult rv = GetCellFromRange(range, aCell);
- // Failure here means the range doesn't contain a cell
- NS_ENSURE_SUCCESS(rv, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- // We found a selected cell
- if (*aCell) {
- break;
- }
- // If we didn't find a cell, continue to next range in selection
- }
- // No cell means all remaining ranges were collapsed (cells were deleted)
- NS_ENSURE_TRUE(*aCell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- if (aRange) {
- *aRange = range.get();
- NS_ADDREF(*aRange);
- }
- // Setup for next cell
- mSelectedCellIndex++;
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::GetFirstSelectedCellInTable(int32_t* aRowIndex,
- int32_t* aColIndex,
- nsIDOMElement** aCell)
- {
- NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
- *aCell = nullptr;
- if (aRowIndex) {
- *aRowIndex = 0;
- }
- if (aColIndex) {
- *aColIndex = 0;
- }
- nsCOMPtr<nsIDOMElement> cell;
- nsresult rv = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
- *aCell = cell.get();
- NS_ADDREF(*aCell);
- // Also return the row and/or column if requested
- if (aRowIndex || aColIndex) {
- int32_t startRowIndex, startColIndex;
- rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (aRowIndex) {
- *aRowIndex = startRowIndex;
- }
- if (aColIndex) {
- *aColIndex = startColIndex;
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::SetSelectionAfterTableEdit(nsIDOMElement* aTable,
- int32_t aRow,
- int32_t aCol,
- int32_t aDirection,
- bool aSelected)
- {
- NS_ENSURE_TRUE(aTable, NS_ERROR_NOT_INITIALIZED);
- RefPtr<Selection> selection = GetSelection();
- if (!selection) {
- return NS_ERROR_FAILURE;
- }
- nsCOMPtr<nsIDOMElement> cell;
- bool done = false;
- do {
- nsresult rv = GetCellAt(aTable, aRow, aCol, getter_AddRefs(cell));
- if (NS_FAILED(rv)) {
- break;
- }
- if (cell) {
- if (aSelected) {
- // Reselect the cell
- return SelectElement(cell);
- } else {
- // Set the caret to deepest first child
- // but don't go into nested tables
- // TODO: Should we really be placing the caret at the END
- // of the cell content?
- nsCOMPtr<nsINode> cellNode = do_QueryInterface(cell);
- if (cellNode) {
- CollapseSelectionToDeepestNonTableFirstChild(selection, cellNode);
- }
- return NS_OK;
- }
- } else {
- // Setup index to find another cell in the
- // direction requested, but move in
- // other direction if already at beginning of row or column
- switch (aDirection) {
- case ePreviousColumn:
- if (!aCol) {
- if (aRow > 0) {
- aRow--;
- } else {
- done = true;
- }
- } else {
- aCol--;
- }
- break;
- case ePreviousRow:
- if (!aRow) {
- if (aCol > 0) {
- aCol--;
- } else {
- done = true;
- }
- } else {
- aRow--;
- }
- break;
- default:
- done = true;
- }
- }
- } while (!done);
- // We didn't find a cell
- // Set selection to just before the table
- nsCOMPtr<nsIDOMNode> tableParent;
- nsresult rv = aTable->GetParentNode(getter_AddRefs(tableParent));
- if (NS_SUCCEEDED(rv) && tableParent) {
- int32_t tableOffset = GetChildOffset(aTable, tableParent);
- return selection->Collapse(tableParent, tableOffset);
- }
- // Last resort: Set selection to start of doc
- // (it's very bad to not have a valid selection!)
- return SetSelectionAtDocumentStart(selection);
- }
- NS_IMETHODIMP
- HTMLEditor::GetSelectedOrParentTableElement(nsAString& aTagName,
- int32_t* aSelectedCount,
- nsIDOMElement** aTableElement)
- {
- NS_ENSURE_ARG_POINTER(aTableElement);
- NS_ENSURE_ARG_POINTER(aSelectedCount);
- *aTableElement = nullptr;
- aTagName.Truncate();
- *aSelectedCount = 0;
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
- // Try to get the first selected cell
- nsCOMPtr<nsIDOMElement> tableOrCellElement;
- nsresult rv = GetFirstSelectedCell(nullptr,
- getter_AddRefs(tableOrCellElement));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_NAMED_LITERAL_STRING(tdName, "td");
- if (tableOrCellElement) {
- // Each cell is in its own selection range,
- // so count signals multiple-cell selection
- rv = selection->GetRangeCount(aSelectedCount);
- NS_ENSURE_SUCCESS(rv, rv);
- aTagName = tdName;
- } else {
- nsCOMPtr<nsIDOMNode> anchorNode;
- rv = selection->GetAnchorNode(getter_AddRefs(anchorNode));
- if (NS_FAILED(rv)) {
- return rv;
- }
- NS_ENSURE_TRUE(anchorNode, NS_ERROR_FAILURE);
- nsCOMPtr<nsIDOMNode> selectedNode;
- // Get child of anchor node, if exists
- bool hasChildren;
- anchorNode->HasChildNodes(&hasChildren);
- if (hasChildren) {
- int32_t anchorOffset;
- rv = selection->GetAnchorOffset(&anchorOffset);
- NS_ENSURE_SUCCESS(rv, rv);
- selectedNode = GetChildAt(anchorNode, anchorOffset);
- if (!selectedNode) {
- selectedNode = anchorNode;
- // If anchor doesn't have a child, we can't be selecting a table element,
- // so don't do the following:
- } else {
- nsCOMPtr<nsIAtom> atom = EditorBase::GetTag(selectedNode);
- if (atom == nsGkAtoms::td) {
- tableOrCellElement = do_QueryInterface(selectedNode);
- aTagName = tdName;
- // Each cell is in its own selection range,
- // so count signals multiple-cell selection
- rv = selection->GetRangeCount(aSelectedCount);
- NS_ENSURE_SUCCESS(rv, rv);
- } else if (atom == nsGkAtoms::table) {
- tableOrCellElement = do_QueryInterface(selectedNode);
- aTagName.AssignLiteral("table");
- *aSelectedCount = 1;
- } else if (atom == nsGkAtoms::tr) {
- tableOrCellElement = do_QueryInterface(selectedNode);
- aTagName.AssignLiteral("tr");
- *aSelectedCount = 1;
- }
- }
- }
- if (!tableOrCellElement) {
- // Didn't find a table element -- find a cell parent
- rv = GetElementOrParentByTagName(tdName, anchorNode,
- getter_AddRefs(tableOrCellElement));
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (tableOrCellElement) {
- aTagName = tdName;
- }
- }
- }
- if (tableOrCellElement) {
- *aTableElement = tableOrCellElement.get();
- NS_ADDREF(*aTableElement);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLEditor::GetSelectedCellsType(nsIDOMElement* aElement,
- uint32_t* aSelectionType)
- {
- NS_ENSURE_ARG_POINTER(aSelectionType);
- *aSelectionType = 0;
- // Be sure we have a table element
- // (if aElement is null, this uses selection's anchor node)
- nsCOMPtr<nsIDOMElement> table;
- nsresult rv =
- GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aElement,
- getter_AddRefs(table));
- NS_ENSURE_SUCCESS(rv, rv);
- int32_t rowCount, colCount;
- rv = GetTableSize(table, &rowCount, &colCount);
- NS_ENSURE_SUCCESS(rv, rv);
- // Traverse all selected cells
- nsCOMPtr<nsIDOMElement> selectedCell;
- rv = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell));
- NS_ENSURE_SUCCESS(rv, rv);
- if (rv == NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND) {
- return NS_OK;
- }
- // We have at least one selected cell, so set return value
- *aSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
- // Store indexes of each row/col to avoid duplication of searches
- nsTArray<int32_t> indexArray;
- bool allCellsInRowAreSelected = false;
- bool allCellsInColAreSelected = false;
- while (NS_SUCCEEDED(rv) && selectedCell) {
- // Get the cell's location in the cellmap
- int32_t startRowIndex, startColIndex;
- rv = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!indexArray.Contains(startColIndex)) {
- indexArray.AppendElement(startColIndex);
- allCellsInRowAreSelected = AllCellsInRowSelected(table, startRowIndex, colCount);
- // We're done as soon as we fail for any row
- if (!allCellsInRowAreSelected) {
- break;
- }
- }
- rv = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
- }
- if (allCellsInRowAreSelected) {
- *aSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
- return NS_OK;
- }
- // Test for columns
- // Empty the indexArray
- indexArray.Clear();
- // Start at first cell again
- rv = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell));
- while (NS_SUCCEEDED(rv) && selectedCell) {
- // Get the cell's location in the cellmap
- int32_t startRowIndex, startColIndex;
- rv = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!indexArray.Contains(startRowIndex)) {
- indexArray.AppendElement(startColIndex);
- allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, rowCount);
- // We're done as soon as we fail for any column
- if (!allCellsInRowAreSelected) {
- break;
- }
- }
- rv = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
- }
- if (allCellsInColAreSelected) {
- *aSelectionType = nsISelectionPrivate::TABLESELECTION_COLUMN;
- }
- return NS_OK;
- }
- bool
- HTMLEditor::AllCellsInRowSelected(nsIDOMElement* aTable,
- int32_t aRowIndex,
- int32_t aNumberOfColumns)
- {
- NS_ENSURE_TRUE(aTable, false);
- int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- for (int32_t col = 0; col < aNumberOfColumns;
- col += std::max(actualColSpan, 1)) {
- nsCOMPtr<nsIDOMElement> cell;
- nsresult rv = GetCellDataAt(aTable, aRowIndex, col, getter_AddRefs(cell),
- &curStartRowIndex, &curStartColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, false);
- // If no cell, we may have a "ragged" right edge,
- // so return TRUE only if we already found a cell in the row
- NS_ENSURE_TRUE(cell, (col > 0) ? true : false);
- // Return as soon as a non-selected cell is found
- NS_ENSURE_TRUE(isSelected, false);
- NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in AllCellsInRowSelected");
- }
- return true;
- }
- bool
- HTMLEditor::AllCellsInColumnSelected(nsIDOMElement* aTable,
- int32_t aColIndex,
- int32_t aNumberOfRows)
- {
- NS_ENSURE_TRUE(aTable, false);
- int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
- bool isSelected;
- for (int32_t row = 0; row < aNumberOfRows;
- row += std::max(actualRowSpan, 1)) {
- nsCOMPtr<nsIDOMElement> cell;
- nsresult rv = GetCellDataAt(aTable, row, aColIndex, getter_AddRefs(cell),
- &curStartRowIndex, &curStartColIndex,
- &rowSpan, &colSpan,
- &actualRowSpan, &actualColSpan, &isSelected);
- NS_ENSURE_SUCCESS(rv, false);
- // If no cell, we must have a "ragged" right edge on the last column
- // so return TRUE only if we already found a cell in the row
- NS_ENSURE_TRUE(cell, (row > 0) ? true : false);
- // Return as soon as a non-selected cell is found
- NS_ENSURE_TRUE(isSelected, false);
- }
- return true;
- }
- bool
- HTMLEditor::IsEmptyCell(dom::Element* aCell)
- {
- MOZ_ASSERT(aCell);
- // Check if target only contains empty text node or <br>
- nsCOMPtr<nsINode> cellChild = aCell->GetFirstChild();
- if (!cellChild) {
- return false;
- }
- nsCOMPtr<nsINode> nextChild = cellChild->GetNextSibling();
- if (nextChild) {
- return false;
- }
- // We insert a single break into a cell by default
- // to have some place to locate a cursor -- it is dispensable
- if (cellChild->IsHTMLElement(nsGkAtoms::br)) {
- return true;
- }
- bool isEmpty;
- // Or check if no real content
- nsresult rv = IsEmptyNode(cellChild, &isEmpty, false, false);
- NS_ENSURE_SUCCESS(rv, false);
- return isEmpty;
- }
- } // namespace mozilla
|