| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/toolbar/toolbar_model.h" | 5 #include "chrome/browser/ui/toolbar/toolbar_model.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 | 31 |
| 32 namespace { | 32 namespace { |
| 33 | 33 |
| 34 struct TestItem { | 34 struct TestItem { |
| 35 GURL url; | 35 GURL url; |
| 36 string16 expected_text; | 36 string16 expected_text; |
| 37 // The expected text to display when query extraction is inactive. | 37 // The expected text to display when query extraction is inactive. |
| 38 string16 expected_replace_text_inactive; | 38 string16 expected_replace_text_inactive; |
| 39 // The expected text to display when query extraction is active. | 39 // The expected text to display when query extraction is active. |
| 40 string16 expected_replace_text_active; | 40 string16 expected_replace_text_active; |
| 41 bool would_replace; | 41 ToolbarModel::SearchTermType search_term_type; |
| 42 bool should_display; | 42 bool should_display; |
| 43 } test_items[] = { | 43 } test_items[] = { |
| 44 { | 44 { |
| 45 GURL("view-source:http://www.google.com"), | 45 GURL("view-source:http://www.google.com"), |
| 46 ASCIIToUTF16("view-source:www.google.com"), | 46 ASCIIToUTF16("view-source:www.google.com"), |
| 47 ASCIIToUTF16("view-source:www.google.com"), | 47 ASCIIToUTF16("view-source:www.google.com"), |
| 48 ASCIIToUTF16("view-source:www.google.com"), | 48 ASCIIToUTF16("view-source:www.google.com"), |
| 49 false, | 49 ToolbarModel::SEARCH_TERM_NONE, |
| 50 true | 50 true |
| 51 }, | 51 }, |
| 52 { | 52 { |
| 53 GURL("view-source:chrome://newtab/"), | 53 GURL("view-source:chrome://newtab/"), |
| 54 ASCIIToUTF16("view-source:chrome://newtab"), | 54 ASCIIToUTF16("view-source:chrome://newtab"), |
| 55 ASCIIToUTF16("view-source:chrome://newtab"), | 55 ASCIIToUTF16("view-source:chrome://newtab"), |
| 56 ASCIIToUTF16("view-source:chrome://newtab"), | 56 ASCIIToUTF16("view-source:chrome://newtab"), |
| 57 false, | 57 ToolbarModel::SEARCH_TERM_NONE, |
| 58 true | 58 true |
| 59 }, | 59 }, |
| 60 { | 60 { |
| 61 GURL("chrome-extension://monkey/balls.html"), | 61 GURL("chrome-extension://monkey/balls.html"), |
| 62 ASCIIToUTF16("chrome-extension://monkey/balls.html"), | 62 ASCIIToUTF16("chrome-extension://monkey/balls.html"), |
| 63 ASCIIToUTF16("chrome-extension://monkey/balls.html"), | 63 ASCIIToUTF16("chrome-extension://monkey/balls.html"), |
| 64 ASCIIToUTF16("chrome-extension://monkey/balls.html"), | 64 ASCIIToUTF16("chrome-extension://monkey/balls.html"), |
| 65 false, | 65 ToolbarModel::SEARCH_TERM_NONE, |
| 66 true | 66 true |
| 67 }, | 67 }, |
| 68 { | 68 { |
| 69 GURL("chrome://newtab/"), | 69 GURL("chrome://newtab/"), |
| 70 string16(), | 70 string16(), |
| 71 string16(), | 71 string16(), |
| 72 string16(), | 72 string16(), |
| 73 false, | 73 ToolbarModel::SEARCH_TERM_NONE, |
| 74 false | 74 false |
| 75 }, | 75 }, |
| 76 { | 76 { |
| 77 GURL(chrome::kAboutBlankURL), | 77 GURL(chrome::kAboutBlankURL), |
| 78 ASCIIToUTF16(chrome::kAboutBlankURL), | 78 ASCIIToUTF16(chrome::kAboutBlankURL), |
| 79 ASCIIToUTF16(chrome::kAboutBlankURL), | 79 ASCIIToUTF16(chrome::kAboutBlankURL), |
| 80 ASCIIToUTF16(chrome::kAboutBlankURL), | 80 ASCIIToUTF16(chrome::kAboutBlankURL), |
| 81 false, | 81 ToolbarModel::SEARCH_TERM_NONE, |
| 82 true | 82 true |
| 83 }, | 83 }, |
| 84 { | 84 { |
| 85 GURL("http://searchurl/?q=tractor+supply"), | 85 GURL("http://searchurl/?q=tractor+supply"), |
| 86 ASCIIToUTF16("searchurl/?q=tractor+supply"), | 86 ASCIIToUTF16("searchurl/?q=tractor+supply"), |
| 87 ASCIIToUTF16("searchurl/?q=tractor+supply"), | 87 ASCIIToUTF16("searchurl/?q=tractor+supply"), |
| 88 ASCIIToUTF16("searchurl/?q=tractor+supply"), | 88 ASCIIToUTF16("searchurl/?q=tractor+supply"), |
| 89 false, | 89 ToolbarModel::SEARCH_TERM_NONE, |
| 90 true | 90 true |
| 91 }, | 91 }, |
| 92 { | 92 { |
| 93 GURL("https://google.ca/search?q=tractor+supply"), | 93 GURL("https://google.ca/search?q=tractor+supply"), |
| 94 ASCIIToUTF16("https://google.ca/search?q=tractor+supply"), | 94 ASCIIToUTF16("https://google.ca/search?q=tractor+supply"), |
| 95 ASCIIToUTF16("https://google.ca/search?q=tractor+supply"), | 95 ASCIIToUTF16("https://google.ca/search?q=tractor+supply"), |
| 96 ASCIIToUTF16("https://google.ca/search?q=tractor+supply"), | 96 ASCIIToUTF16("https://google.ca/search?q=tractor+supply"), |
| 97 false, | 97 ToolbarModel::SEARCH_TERM_NONE, |
| 98 true | 98 true |
| 99 }, | 99 }, |
| 100 { | 100 { |
| 101 GURL("https://google.com/search?q=tractor+supply"), | 101 GURL("https://google.com/search?q=tractor+supply"), |
| 102 ASCIIToUTF16("https://google.com/search?q=tractor+supply"), | 102 ASCIIToUTF16("https://google.com/search?q=tractor+supply"), |
| 103 ASCIIToUTF16("https://google.com/search?q=tractor+supply"), | 103 ASCIIToUTF16("https://google.com/search?q=tractor+supply"), |
| 104 ASCIIToUTF16("https://google.com/search?q=tractor+supply"), | 104 ASCIIToUTF16("https://google.com/search?q=tractor+supply"), |
| 105 false, | 105 ToolbarModel::SEARCH_TERM_NONE, |
| 106 true | 106 true |
| 107 }, | 107 }, |
| 108 { | 108 { |
| 109 GURL("https://google.com/search?q=tractor+supply&espv=1"), | 109 GURL("https://google.com/search?q=tractor+supply&espv=1"), |
| 110 ASCIIToUTF16("https://google.com/search?q=tractor+supply&espv=1"), | 110 ASCIIToUTF16("https://google.com/search?q=tractor+supply&espv=1"), |
| 111 ASCIIToUTF16("https://google.com/search?q=tractor+supply&espv=1"), | 111 ASCIIToUTF16("https://google.com/search?q=tractor+supply&espv=1"), |
| 112 ASCIIToUTF16("tractor supply"), | 112 ASCIIToUTF16("tractor supply"), |
| 113 true, | 113 ToolbarModel::SEARCH_TERM_NORMAL, |
| 114 true | 114 true |
| 115 }, | 115 }, |
| 116 { | 116 { |
| 117 GURL("https://google.com/search?q=tractorsupply.com&espv=1"), | 117 GURL("https://google.com/search?q=tractorsupply.com&espv=1"), |
| 118 ASCIIToUTF16("https://google.com/search?q=tractorsupply.com&espv=1"), | 118 ASCIIToUTF16("https://google.com/search?q=tractorsupply.com&espv=1"), |
| 119 ASCIIToUTF16("https://google.com/search?q=tractorsupply.com&espv=1"), | 119 ASCIIToUTF16("https://google.com/search?q=tractorsupply.com&espv=1"), |
| 120 ASCIIToUTF16("https://google.com/search?q=tractorsupply.com&espv=1"), | 120 ASCIIToUTF16("https://google.com/search?q=tractorsupply.com&espv=1"), |
| 121 false, | 121 ToolbarModel::SEARCH_TERM_NONE, |
| 122 true | 122 true |
| 123 }, | 123 }, |
| 124 { | 124 { |
| 125 GURL("https://google.com/search?q=ftp://tractorsupply.ie&espv=1"), | 125 GURL("https://google.com/search?q=ftp://tractorsupply.ie&espv=1"), |
| 126 ASCIIToUTF16("https://google.com/search?q=ftp://tractorsupply.ie&espv=1"), | 126 ASCIIToUTF16("https://google.com/search?q=ftp://tractorsupply.ie&espv=1"), |
| 127 ASCIIToUTF16("https://google.com/search?q=ftp://tractorsupply.ie&espv=1"), | 127 ASCIIToUTF16("https://google.com/search?q=ftp://tractorsupply.ie&espv=1"), |
| 128 ASCIIToUTF16("https://google.com/search?q=ftp://tractorsupply.ie&espv=1"), | 128 ASCIIToUTF16("https://google.com/search?q=ftp://tractorsupply.ie&espv=1"), |
| 129 false, | 129 ToolbarModel::SEARCH_TERM_NONE, |
| 130 true | 130 true |
| 131 }, | 131 }, |
| 132 }; | 132 }; |
| 133 | 133 |
| 134 } // end namespace | 134 } // end namespace |
| 135 | 135 |
| 136 class ToolbarModelTest : public BrowserWithTestWindowTest { | 136 class ToolbarModelTest : public BrowserWithTestWindowTest { |
| 137 public: | 137 public: |
| 138 ToolbarModelTest() {} | 138 ToolbarModelTest() {} |
| 139 | 139 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 158 TemplateURLServiceFactory::GetForProfile(profile()); | 158 TemplateURLServiceFactory::GetForProfile(profile()); |
| 159 template_url_service->Add(search_template_url); | 159 template_url_service->Add(search_template_url); |
| 160 template_url_service->SetDefaultSearchProvider(search_template_url); | 160 template_url_service->SetDefaultSearchProvider(search_template_url); |
| 161 ASSERT_NE(0, search_template_url->id()); | 161 ASSERT_NE(0, search_template_url->id()); |
| 162 template_url_service->Load(); | 162 template_url_service->Load(); |
| 163 } | 163 } |
| 164 | 164 |
| 165 void NavigateAndCheckText(const GURL& url, | 165 void NavigateAndCheckText(const GURL& url, |
| 166 const string16& expected_text, | 166 const string16& expected_text, |
| 167 const string16& expected_replace_text, | 167 const string16& expected_replace_text, |
| 168 bool would_replace, | 168 ToolbarModel::SearchTermType search_term_type, |
| 169 bool should_display) { | 169 bool should_display) { |
| 170 NavigateAndCheckTextImpl(url, false, expected_text, would_replace, | 170 NavigateAndCheckTextImpl(url, false, expected_text, search_term_type, |
| 171 should_display); | 171 should_display); |
| 172 NavigateAndCheckTextImpl(url, true, expected_replace_text, would_replace, | 172 NavigateAndCheckTextImpl(url, true, expected_replace_text, search_term_type, |
| 173 should_display); | 173 should_display); |
| 174 } | 174 } |
| 175 | 175 |
| 176 void NavigateAndCheckQueries(const std::vector<const char*>& queries, | 176 void NavigateAndCheckQueries(const std::vector<const char*>& queries, |
| 177 bool would_replace) { | 177 ToolbarModel::SearchTermType search_term_type) { |
| 178 const std::string kInstantExtendedPrefix( | 178 const std::string kInstantExtendedPrefix( |
| 179 "https://google.com/search?espv=1&q="); | 179 "https://google.com/search?espv=1&q="); |
| 180 | 180 |
| 181 chrome::EnableInstantExtendedAPIForTesting(); | 181 chrome::EnableInstantExtendedAPIForTesting(); |
| 182 | 182 |
| 183 ResetDefaultTemplateURL(); | 183 ResetDefaultTemplateURL(); |
| 184 AddTab(browser(), GURL(chrome::kAboutBlankURL)); | 184 AddTab(browser(), GURL(chrome::kAboutBlankURL)); |
| 185 for (size_t i = 0; i < queries.size(); ++i) { | 185 for (size_t i = 0; i < queries.size(); ++i) { |
| 186 std::string url_string = kInstantExtendedPrefix + | 186 std::string url_string = kInstantExtendedPrefix + |
| 187 net::EscapeQueryParamValue(queries[i], true); | 187 net::EscapeQueryParamValue(queries[i], true); |
| 188 std::string expected_text; |
| 189 if (search_term_type == ToolbarModel::SEARCH_TERM_NONE) |
| 190 expected_text = url_string; |
| 191 else |
| 192 expected_text = std::string(queries[i]); |
| 188 NavigateAndCheckTextImpl(GURL(url_string), true, | 193 NavigateAndCheckTextImpl(GURL(url_string), true, |
| 189 ASCIIToUTF16(would_replace ? | 194 ASCIIToUTF16(expected_text), |
| 190 std::string(queries[i]) : | 195 search_term_type, true); |
| 191 url_string), | |
| 192 would_replace, true); | |
| 193 } | 196 } |
| 194 } | 197 } |
| 195 | 198 |
| 196 private: | 199 private: |
| 197 void NavigateAndCheckTextImpl(const GURL& url, | 200 void NavigateAndCheckTextImpl(const GURL& url, |
| 198 bool can_replace, | 201 bool can_replace, |
| 199 const string16 expected_text, | 202 const string16 expected_text, |
| 200 bool would_replace, | 203 ToolbarModel::SearchTermType search_term_type, |
| 201 bool should_display) { | 204 bool should_display) { |
| 202 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); | 205 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); |
| 203 browser()->OpenURL(OpenURLParams( | 206 browser()->OpenURL(OpenURLParams( |
| 204 url, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, | 207 url, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, |
| 205 false)); | 208 false)); |
| 206 | 209 |
| 207 // Query terms replacement requires that the renderer process be a | 210 // Query terms replacement requires that the renderer process be a |
| 208 // recognized Instant renderer. Fake it. | 211 // recognized Instant renderer. Fake it. |
| 209 InstantService* instant_service = | 212 InstantService* instant_service = |
| 210 InstantServiceFactory::GetForProfile(profile()); | 213 InstantServiceFactory::GetForProfile(profile()); |
| 211 int process_id = contents->GetRenderProcessHost()->GetID(); | 214 int process_id = contents->GetRenderProcessHost()->GetID(); |
| 212 instant_service->AddInstantProcess(process_id); | 215 instant_service->AddInstantProcess(process_id); |
| 213 | 216 |
| 214 ToolbarModel* toolbar_model = browser()->toolbar_model(); | 217 ToolbarModel* toolbar_model = browser()->toolbar_model(); |
| 215 | 218 |
| 216 // Check while loading. | 219 // Check while loading. |
| 217 EXPECT_EQ(should_display, toolbar_model->ShouldDisplayURL()); | 220 EXPECT_EQ(should_display, toolbar_model->ShouldDisplayURL()); |
| 218 EXPECT_EQ(expected_text, toolbar_model->GetText(can_replace)); | 221 EXPECT_EQ(expected_text, toolbar_model->GetText(can_replace)); |
| 219 EXPECT_EQ(would_replace, | 222 EXPECT_EQ(search_term_type, toolbar_model->GetSearchTermType()); |
| 220 toolbar_model->WouldReplaceSearchURLWithSearchTerms()); | |
| 221 | 223 |
| 222 // Check after commit. | 224 // Check after commit. |
| 223 CommitPendingLoad(&contents->GetController()); | 225 CommitPendingLoad(&contents->GetController()); |
| 224 EXPECT_EQ(should_display, toolbar_model->ShouldDisplayURL()); | 226 EXPECT_EQ(should_display, toolbar_model->ShouldDisplayURL()); |
| 225 EXPECT_EQ(expected_text, toolbar_model->GetText(can_replace)); | 227 EXPECT_EQ(expected_text, toolbar_model->GetText(can_replace)); |
| 226 EXPECT_EQ(would_replace, | 228 EXPECT_EQ(search_term_type, toolbar_model->GetSearchTermType()); |
| 227 toolbar_model->WouldReplaceSearchURLWithSearchTerms()); | |
| 228 } | 229 } |
| 229 }; | 230 }; |
| 230 | 231 |
| 231 // Test that we don't replace any URLs when the query extraction is disabled. | 232 // Test that we don't replace any URLs when the query extraction is disabled. |
| 232 TEST_F(ToolbarModelTest, ShouldDisplayURLQueryExtractionDisabled) { | 233 TEST_F(ToolbarModelTest, ShouldDisplayURLQueryExtractionDisabled) { |
| 233 ASSERT_FALSE(chrome::IsQueryExtractionEnabled()) | 234 ASSERT_FALSE(chrome::IsQueryExtractionEnabled()) |
| 234 << "This test expects query extraction to be disabled."; | 235 << "This test expects query extraction to be disabled."; |
| 235 | 236 |
| 236 ResetDefaultTemplateURL(); | 237 ResetDefaultTemplateURL(); |
| 237 AddTab(browser(), GURL(chrome::kAboutBlankURL)); | 238 AddTab(browser(), GURL(chrome::kAboutBlankURL)); |
| 238 for (size_t i = 0; i < arraysize(test_items); ++i) { | 239 for (size_t i = 0; i < arraysize(test_items); ++i) { |
| 239 const TestItem& test_item = test_items[i]; | 240 const TestItem& test_item = test_items[i]; |
| 240 NavigateAndCheckText(test_item.url, | 241 NavigateAndCheckText(test_item.url, |
| 241 test_item.expected_text, | 242 test_item.expected_text, |
| 242 test_item.expected_replace_text_inactive, | 243 test_item.expected_replace_text_inactive, |
| 243 false, | 244 ToolbarModel::SEARCH_TERM_NONE, |
| 244 test_item.should_display); | 245 test_item.should_display); |
| 245 } | 246 } |
| 246 } | 247 } |
| 247 | 248 |
| 248 // Test that we replace URLs when the query extraction API is enabled. | 249 // Test that we replace URLs when the query extraction API is enabled. |
| 249 TEST_F(ToolbarModelTest, ShouldDisplayURLQueryExtractionEnabled) { | 250 TEST_F(ToolbarModelTest, ShouldDisplayURLQueryExtractionEnabled) { |
| 250 chrome::EnableInstantExtendedAPIForTesting(); | 251 chrome::EnableInstantExtendedAPIForTesting(); |
| 251 | 252 |
| 252 ResetDefaultTemplateURL(); | 253 ResetDefaultTemplateURL(); |
| 253 AddTab(browser(), GURL(chrome::kAboutBlankURL)); | 254 AddTab(browser(), GURL(chrome::kAboutBlankURL)); |
| 254 for (size_t i = 0; i < arraysize(test_items); ++i) { | 255 for (size_t i = 0; i < arraysize(test_items); ++i) { |
| 255 const TestItem& test_item = test_items[i]; | 256 const TestItem& test_item = test_items[i]; |
| 256 NavigateAndCheckText(test_item.url, | 257 NavigateAndCheckText(test_item.url, |
| 257 test_item.expected_text, | 258 test_item.expected_text, |
| 258 test_item.expected_replace_text_active, | 259 test_item.expected_replace_text_active, |
| 259 test_item.would_replace, | 260 test_item.search_term_type, |
| 260 test_item.should_display); | 261 test_item.should_display); |
| 261 } | 262 } |
| 262 } | 263 } |
| 263 | 264 |
| 264 // Test that replacement doesn't happen for URLs. | 265 // Test that replacement doesn't happen for URLs. |
| 265 TEST_F(ToolbarModelTest, DoNotExtractUrls) { | 266 TEST_F(ToolbarModelTest, DoNotExtractUrls) { |
| 266 static const char* const kQueries[] = { | 267 static const char* const kQueries[] = { |
| 267 "www.google.com ", | 268 "www.google.com ", |
| 268 "www.google.ca", // Oh, Canada! | 269 "www.google.ca", // Oh, Canada! |
| 269 "www.google.ca.", | 270 "www.google.ca.", |
| 270 "www.google.ca ", | 271 "www.google.ca ", |
| 271 "www.google.ca. ", | 272 "www.google.ca. ", |
| 272 "something.xxx", | 273 "something.xxx", |
| 273 "something.travel", | 274 "something.travel", |
| 274 "bankofamerica.com/", | 275 "bankofamerica.com/", |
| 275 "bankofamerica.com/foo", | 276 "bankofamerica.com/foo", |
| 276 "bankofamerica.com/foo bla", | 277 "bankofamerica.com/foo bla", |
| 277 "BankOfAmerica.BIZ/foo bla", | 278 "BankOfAmerica.BIZ/foo bla", |
| 278 " http:/monkeys", | 279 " http:/monkeys", |
| 279 "http://monkeys", | 280 "http://monkeys", |
| 280 "javascript:alert(1)", | 281 "javascript:alert(1)", |
| 281 "JavaScript:alert(1)", | 282 "JavaScript:alert(1)", |
| 282 "localhost", | 283 "localhost", |
| 283 }; | 284 }; |
| 284 | 285 |
| 285 NavigateAndCheckQueries( | 286 NavigateAndCheckQueries( |
| 286 std::vector<const char*>(&kQueries[0], | 287 std::vector<const char*>(&kQueries[0], &kQueries[arraysize(kQueries)]), |
| 287 &kQueries[arraysize(kQueries)]), false); | 288 ToolbarModel::SEARCH_TERM_NONE); |
| 288 } | 289 } |
| 289 | 290 |
| 290 // Test that replacement does happen for non-URLs. | 291 // Test that replacement does happen for non-URLs. |
| 291 TEST_F(ToolbarModelTest, ExtractNonUrls) { | 292 TEST_F(ToolbarModelTest, ExtractNonUrls) { |
| 292 static const char* const kQueries[] = { | 293 static const char* const kQueries[] = { |
| 293 "facebook.com login", | 294 "facebook.com login", |
| 294 "site:amazon.com", | 295 "site:amazon.com", |
| 295 "e.coli", | 296 "e.coli", |
| 296 "info:http://www.google.com/", | 297 "info:http://www.google.com/", |
| 297 "cache:http://www.apple.com/", | 298 "cache:http://www.apple.com/", |
| 298 "9/11", | 299 "9/11", |
| 299 "<nomatch/>", | 300 "<nomatch/>", |
| 300 "ac/dc", | 301 "ac/dc", |
| 301 "ps/2", | 302 "ps/2", |
| 302 }; | 303 }; |
| 303 | 304 |
| 304 NavigateAndCheckQueries( | 305 NavigateAndCheckQueries( |
| 305 std::vector<const char*>(&kQueries[0], | 306 std::vector<const char*>(&kQueries[0], &kQueries[arraysize(kQueries)]), |
| 306 &kQueries[arraysize(kQueries)]), true); | 307 ToolbarModel::SEARCH_TERM_NORMAL); |
| 307 } | 308 } |
| 309 |
| 310 // Verify that URL search terms are correctly identified. |
| 311 TEST_F(ToolbarModelTest, ProminentSearchTerm) { |
| 312 static const char* const kQueries[] = { |
| 313 "example.com" |
| 314 }; |
| 315 browser()->toolbar_model()->SetIsProminentSearchTermUISupported(true); |
| 316 NavigateAndCheckQueries( |
| 317 std::vector<const char*>(&kQueries[0], &kQueries[arraysize(kQueries)]), |
| 318 ToolbarModel::SEARCH_TERM_PROMINENT); |
| 319 } |
| OLD | NEW |