From f9d60598992f3d9ce2f4b5860a7835fa44d55c88 Mon Sep 17 00:00:00 2001 From: Jane Liu Date: Tue, 25 Jul 2017 18:02:50 -0400 Subject: Added CPDF_NameTree::DeleteValueAndName() 1. Added CPDF_NameTree::DeleteValueAndName() for deleting new name and value pairs from a nametree. This function will be used for the API for deleting embedded files. * Added an anonymous helper function for updating the tree nodes and limits upon deletion. * Added a unit test. Bug=pdfium:174 Change-Id: I3ee4c27af637f721ee0ccda26fbae3d3a5e58f5e Reviewed-on: https://pdfium-review.googlesource.com/8931 Reviewed-by: dsinclair Commit-Queue: dsinclair --- core/fpdfdoc/cpdf_nametree_unittest.cpp | 160 +++++++++++++++++++++++++------- 1 file changed, 127 insertions(+), 33 deletions(-) (limited to 'core/fpdfdoc/cpdf_nametree_unittest.cpp') diff --git a/core/fpdfdoc/cpdf_nametree_unittest.cpp b/core/fpdfdoc/cpdf_nametree_unittest.cpp index 4842f06a8e..e6e188a72c 100644 --- a/core/fpdfdoc/cpdf_nametree_unittest.cpp +++ b/core/fpdfdoc/cpdf_nametree_unittest.cpp @@ -41,6 +41,37 @@ void CheckLimitsArray(CPDF_Dictionary* pNode, EXPECT_STREQ(greatest, pLimits->GetStringAt(1).c_str()); } +void FillNameTreeDict(CPDF_Dictionary* pRootDict) { + CPDF_Array* pKids = pRootDict->SetNewFor("Kids"); + CPDF_Dictionary* pKid1 = pKids->AddNew(); + + // Make the lower and upper limit out of order on purpose. + AddLimitsArray(pKid1, "9.txt", "1.txt"); + pKids = pKid1->SetNewFor("Kids"); + CPDF_Dictionary* pKid2 = pKids->AddNew(); + CPDF_Dictionary* pKid3 = pKids->AddNew(); + + AddLimitsArray(pKid2, "1.txt", "5.txt"); + pKids = pKid2->SetNewFor("Kids"); + CPDF_Dictionary* pKid4 = pKids->AddNew(); + CPDF_Dictionary* pKid5 = pKids->AddNew(); + + AddLimitsArray(pKid3, "9.txt", "9.txt"); + CPDF_Array* pNames = pKid3->SetNewFor("Names"); + AddNameKeyValue(pNames, "9.txt", 999); + + // Make the lower and upper limit out of order on purpose. + AddLimitsArray(pKid4, "2.txt", "1.txt"); + pNames = pKid4->SetNewFor("Names"); + AddNameKeyValue(pNames, "1.txt", 111); + AddNameKeyValue(pNames, "2.txt", 222); + + AddLimitsArray(pKid5, "3.txt", "5.txt"); + pNames = pKid5->SetNewFor("Names"); + AddNameKeyValue(pNames, "3.txt", 333); + AddNameKeyValue(pNames, "5.txt", 555); +} + } // namespace TEST(cpdf_nametree, GetUnicodeNameWithBOM) { @@ -106,33 +137,7 @@ TEST(cpdf_nametree, AddIntoNames) { TEST(cpdf_nametree, AddIntoKids) { // Set up a name tree with five nodes of three levels. auto pRootDict = pdfium::MakeUnique(); - CPDF_Array* pKids = pRootDict->SetNewFor("Kids"); - CPDF_Dictionary* pKid1 = pKids->AddNew(); - - AddLimitsArray(pKid1, "1.txt", "9.txt"); - pKids = pKid1->SetNewFor("Kids"); - CPDF_Dictionary* pKid2 = pKids->AddNew(); - CPDF_Dictionary* pKid3 = pKids->AddNew(); - - AddLimitsArray(pKid2, "1.txt", "5.txt"); - pKids = pKid2->SetNewFor("Kids"); - CPDF_Dictionary* pKid4 = pKids->AddNew(); - CPDF_Dictionary* pKid5 = pKids->AddNew(); - - AddLimitsArray(pKid3, "9.txt", "9.txt"); - CPDF_Array* pNames = pKid3->SetNewFor("Names"); - AddNameKeyValue(pNames, "9.txt", 999); - - AddLimitsArray(pKid4, "1.txt", "2.txt"); - pNames = pKid4->SetNewFor("Names"); - AddNameKeyValue(pNames, "1.txt", 111); - AddNameKeyValue(pNames, "2.txt", 222); - - AddLimitsArray(pKid5, "3.txt", "5.txt"); - pNames = pKid5->SetNewFor("Names"); - AddNameKeyValue(pNames, "3.txt", 333); - AddNameKeyValue(pNames, "5.txt", 555); - + FillNameTreeDict(pRootDict.get()); CPDF_NameTree nameTree(pRootDict.get()); // Check that adding an existing name would fail. @@ -164,25 +169,26 @@ TEST(cpdf_nametree, AddIntoKids) { EXPECT_EQ(-5, nameTree.LookupValue(L"0.txt")->GetInteger()); // Check that the node on the first level has the expected limits. - pKid1 = nameTree.GetRoot()->GetArrayFor("Kids")->GetDictAt(0); + CPDF_Dictionary* pKid1 = + nameTree.GetRoot()->GetArrayFor("Kids")->GetDictAt(0); ASSERT_TRUE(pKid1); CheckLimitsArray(pKid1, "0.txt", "99.txt"); // Check that the nodes on the second level has the expected limits and names. - pKid2 = pKid1->GetArrayFor("Kids")->GetDictAt(0); + CPDF_Dictionary* pKid2 = pKid1->GetArrayFor("Kids")->GetDictAt(0); ASSERT_TRUE(pKid2); CheckLimitsArray(pKid2, "0.txt", "6.txt"); - pKid3 = pKid1->GetArrayFor("Kids")->GetDictAt(1); + CPDF_Dictionary* pKid3 = pKid1->GetArrayFor("Kids")->GetDictAt(1); ASSERT_TRUE(pKid3); CheckLimitsArray(pKid3, "9.txt", "99.txt"); - pNames = pKid3->GetArrayFor("Names"); + CPDF_Array* pNames = pKid3->GetArrayFor("Names"); ASSERT_TRUE(pNames); CheckNameKeyValue(pNames, 0, "9.txt", 999); CheckNameKeyValue(pNames, 1, "99.txt", 99); // Check that the nodes on the third level has the expected limits and names. - pKid4 = pKid2->GetArrayFor("Kids")->GetDictAt(0); + CPDF_Dictionary* pKid4 = pKid2->GetArrayFor("Kids")->GetDictAt(0); ASSERT_TRUE(pKid4); CheckLimitsArray(pKid4, "0.txt", "2.txt"); pNames = pKid4->GetArrayFor("Names"); @@ -191,7 +197,7 @@ TEST(cpdf_nametree, AddIntoKids) { CheckNameKeyValue(pNames, 1, "1.txt", 111); CheckNameKeyValue(pNames, 2, "2.txt", 222); - pKid5 = pKid2->GetArrayFor("Kids")->GetDictAt(1); + CPDF_Dictionary* pKid5 = pKid2->GetArrayFor("Kids")->GetDictAt(1); ASSERT_TRUE(pKid5); CheckLimitsArray(pKid5, "3.txt", "6.txt"); pNames = pKid5->GetArrayFor("Names"); @@ -201,3 +207,91 @@ TEST(cpdf_nametree, AddIntoKids) { CheckNameKeyValue(pNames, 2, "5.txt", 555); CheckNameKeyValue(pNames, 3, "6.txt", 666); } + +TEST(cpdf_nametree, DeleteFromKids) { + // Set up a name tree with five nodes of three levels. + auto pRootDict = pdfium::MakeUnique(); + FillNameTreeDict(pRootDict.get()); + CPDF_NameTree nameTree(pRootDict.get()); + + // Retrieve the kid dictionaries. + CPDF_Dictionary* pKid1 = + nameTree.GetRoot()->GetArrayFor("Kids")->GetDictAt(0); + ASSERT_TRUE(pKid1); + CPDF_Dictionary* pKid2 = pKid1->GetArrayFor("Kids")->GetDictAt(0); + ASSERT_TRUE(pKid2); + CPDF_Dictionary* pKid3 = pKid1->GetArrayFor("Kids")->GetDictAt(1); + ASSERT_TRUE(pKid3); + CPDF_Dictionary* pKid4 = pKid2->GetArrayFor("Kids")->GetDictAt(0); + ASSERT_TRUE(pKid4); + CPDF_Dictionary* pKid5 = pKid2->GetArrayFor("Kids")->GetDictAt(1); + ASSERT_TRUE(pKid5); + + // Check that deleting an out-of-bound index would fail. + EXPECT_FALSE(nameTree.DeleteValueAndName(5)); + + // Delete the name "9.txt", and check that its node gets deleted and its + // parent node's limits get updated. + CFX_WideString csName; + ASSERT_TRUE(nameTree.LookupValue(L"9.txt")); + EXPECT_EQ(999, nameTree.LookupValue(L"9.txt")->GetInteger()); + EXPECT_TRUE(nameTree.LookupValueAndName(4, &csName)); + EXPECT_STREQ(L"9.txt", csName.c_str()); + EXPECT_EQ(2u, pKid1->GetArrayFor("Kids")->GetCount()); + EXPECT_TRUE(nameTree.DeleteValueAndName(4)); + EXPECT_EQ(1u, pKid1->GetArrayFor("Kids")->GetCount()); + CheckLimitsArray(pKid1, "1.txt", "5.txt"); + + // Delete the name "2.txt", and check that its node does not get deleted, its + // node's limits get updated, and no other limits get updated. + ASSERT_TRUE(nameTree.LookupValue(L"2.txt")); + EXPECT_EQ(222, nameTree.LookupValue(L"2.txt")->GetInteger()); + EXPECT_TRUE(nameTree.LookupValueAndName(1, &csName)); + EXPECT_STREQ(L"2.txt", csName.c_str()); + EXPECT_EQ(4u, pKid4->GetArrayFor("Names")->GetCount()); + EXPECT_TRUE(nameTree.DeleteValueAndName(1)); + EXPECT_EQ(2u, pKid4->GetArrayFor("Names")->GetCount()); + CheckLimitsArray(pKid4, "1.txt", "1.txt"); + CheckLimitsArray(pKid2, "1.txt", "5.txt"); + CheckLimitsArray(pKid1, "1.txt", "5.txt"); + + // Delete the name "1.txt", and check that its node gets deleted, and its + // parent's and gradparent's limits get updated. + ASSERT_TRUE(nameTree.LookupValue(L"1.txt")); + EXPECT_EQ(111, nameTree.LookupValue(L"1.txt")->GetInteger()); + EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName)); + EXPECT_STREQ(L"1.txt", csName.c_str()); + EXPECT_EQ(2u, pKid2->GetArrayFor("Kids")->GetCount()); + EXPECT_TRUE(nameTree.DeleteValueAndName(0)); + EXPECT_EQ(1u, pKid2->GetArrayFor("Kids")->GetCount()); + CheckLimitsArray(pKid2, "3.txt", "5.txt"); + CheckLimitsArray(pKid1, "3.txt", "5.txt"); + + // Delete the name "3.txt", and check that its node does not get deleted, and + // its node's, its parent's, and its grandparent's limits get updated. + ASSERT_TRUE(nameTree.LookupValue(L"3.txt")); + EXPECT_EQ(333, nameTree.LookupValue(L"3.txt")->GetInteger()); + EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName)); + EXPECT_STREQ(L"3.txt", csName.c_str()); + EXPECT_EQ(4u, pKid5->GetArrayFor("Names")->GetCount()); + EXPECT_TRUE(nameTree.DeleteValueAndName(0)); + EXPECT_EQ(2u, pKid5->GetArrayFor("Names")->GetCount()); + CheckLimitsArray(pKid5, "5.txt", "5.txt"); + CheckLimitsArray(pKid2, "5.txt", "5.txt"); + CheckLimitsArray(pKid1, "5.txt", "5.txt"); + + // Delete the name "5.txt", and check that all nodes in the tree get deleted + // since they are now all empty. + ASSERT_TRUE(nameTree.LookupValue(L"5.txt")); + EXPECT_EQ(555, nameTree.LookupValue(L"5.txt")->GetInteger()); + EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName)); + EXPECT_STREQ(L"5.txt", csName.c_str()); + EXPECT_EQ(1u, nameTree.GetRoot()->GetArrayFor("Kids")->GetCount()); + EXPECT_TRUE(nameTree.DeleteValueAndName(0)); + EXPECT_EQ(0u, nameTree.GetRoot()->GetArrayFor("Kids")->GetCount()); + + // Check that the tree is now empty. + EXPECT_EQ(0u, nameTree.GetCount()); + EXPECT_FALSE(nameTree.LookupValueAndName(0, &csName)); + EXPECT_FALSE(nameTree.DeleteValueAndName(0)); +} -- cgit v1.2.3