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/gtk/download/download_item_gtk.h" | 5 #include "chrome/browser/ui/gtk/download/download_item_gtk.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/callback.h" | 8 #include "base/callback.h" |
9 #include "base/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
186 gtk_box_pack_start(GTK_BOX(shelf_hbox), hbox_.get(), FALSE, FALSE, 0); | 186 gtk_box_pack_start(GTK_BOX(shelf_hbox), hbox_.get(), FALSE, FALSE, 0); |
187 // Insert as the leftmost item. | 187 // Insert as the leftmost item. |
188 gtk_box_reorder_child(GTK_BOX(shelf_hbox), hbox_.get(), 0); | 188 gtk_box_reorder_child(GTK_BOX(shelf_hbox), hbox_.get(), 0); |
189 | 189 |
190 get_download()->AddObserver(this); | 190 get_download()->AddObserver(this); |
191 | 191 |
192 new_item_animation_.reset(new ui::SlideAnimation(this)); | 192 new_item_animation_.reset(new ui::SlideAnimation(this)); |
193 new_item_animation_->SetSlideDuration(kNewItemAnimationDurationMs); | 193 new_item_animation_->SetSlideDuration(kNewItemAnimationDurationMs); |
194 gtk_widget_show_all(hbox_.get()); | 194 gtk_widget_show_all(hbox_.get()); |
195 | 195 |
196 if (IsDangerous()) { | 196 if (download_model_->IsDangerous()) { |
197 // Hide the download item components for now. | 197 // Hide the download item components for now. |
198 gtk_widget_set_no_show_all(body_.get(), TRUE); | 198 gtk_widget_set_no_show_all(body_.get(), TRUE); |
199 gtk_widget_set_no_show_all(menu_button_, TRUE); | 199 gtk_widget_set_no_show_all(menu_button_, TRUE); |
200 gtk_widget_hide(body_.get()); | 200 gtk_widget_hide(body_.get()); |
201 gtk_widget_hide(menu_button_); | 201 gtk_widget_hide(menu_button_); |
202 | 202 |
203 // Create an hbox to hold it all. | 203 // Create an hbox to hold it all. |
204 dangerous_hbox_.Own(gtk_hbox_new(FALSE, kDangerousElementPadding)); | 204 dangerous_hbox_.Own(gtk_hbox_new(FALSE, kDangerousElementPadding)); |
205 | 205 |
206 // Add padding at the beginning and end. The hbox will add padding between | 206 // Add padding at the beginning and end. The hbox will add padding between |
(...skipping 19 matching lines...) Expand all Loading... |
226 // Create the nevermind button. | 226 // Create the nevermind button. |
227 GtkWidget* dangerous_decline = gtk_button_new_with_label( | 227 GtkWidget* dangerous_decline = gtk_button_new_with_label( |
228 l10n_util::GetStringUTF8(IDS_DISCARD_DOWNLOAD).c_str()); | 228 l10n_util::GetStringUTF8(IDS_DISCARD_DOWNLOAD).c_str()); |
229 g_signal_connect(dangerous_decline, "clicked", | 229 g_signal_connect(dangerous_decline, "clicked", |
230 G_CALLBACK(OnDangerousDeclineThunk), this); | 230 G_CALLBACK(OnDangerousDeclineThunk), this); |
231 gtk_util::CenterWidgetInHBox(dangerous_hbox_.get(), dangerous_decline, | 231 gtk_util::CenterWidgetInHBox(dangerous_hbox_.get(), dangerous_decline, |
232 false, 0); | 232 false, 0); |
233 | 233 |
234 // Create the ok button. | 234 // Create the ok button. |
235 GtkWidget* dangerous_accept = gtk_button_new_with_label( | 235 GtkWidget* dangerous_accept = gtk_button_new_with_label( |
236 l10n_util::GetStringUTF8( | 236 UTF16ToUTF8(download_model_->GetWarningConfirmButtonText()).c_str()); |
237 ChromeDownloadManagerDelegate::IsExtensionDownload( | |
238 download_model->download()) ? | |
239 IDS_CONTINUE_EXTENSION_DOWNLOAD : | |
240 IDS_CONFIRM_DOWNLOAD).c_str()); | |
241 g_signal_connect(dangerous_accept, "clicked", | 237 g_signal_connect(dangerous_accept, "clicked", |
242 G_CALLBACK(OnDangerousAcceptThunk), this); | 238 G_CALLBACK(OnDangerousAcceptThunk), this); |
243 gtk_util::CenterWidgetInHBox(dangerous_hbox_.get(), dangerous_accept, false, | 239 gtk_util::CenterWidgetInHBox(dangerous_hbox_.get(), dangerous_accept, false, |
244 0); | 240 0); |
245 | 241 |
246 // Put it in an alignment so that padding will be added on the left and | 242 // Put it in an alignment so that padding will be added on the left and |
247 // right. | 243 // right. |
248 dangerous_prompt_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); | 244 dangerous_prompt_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); |
249 gtk_alignment_set_padding(GTK_ALIGNMENT(dangerous_prompt_), | 245 gtk_alignment_set_padding(GTK_ALIGNMENT(dangerous_prompt_), |
250 0, 0, kDangerousElementPadding, kDangerousElementPadding); | 246 0, 0, kDangerousElementPadding, kDangerousElementPadding); |
251 gtk_container_add(GTK_CONTAINER(dangerous_prompt_), dangerous_hbox_.get()); | 247 gtk_container_add(GTK_CONTAINER(dangerous_prompt_), dangerous_hbox_.get()); |
252 gtk_box_pack_start(GTK_BOX(hbox_.get()), dangerous_prompt_, FALSE, FALSE, | 248 gtk_box_pack_start(GTK_BOX(hbox_.get()), dangerous_prompt_, FALSE, FALSE, |
253 0); | 249 0); |
254 gtk_widget_set_app_paintable(dangerous_prompt_, TRUE); | 250 gtk_widget_set_app_paintable(dangerous_prompt_, TRUE); |
255 gtk_widget_set_redraw_on_allocate(dangerous_prompt_, TRUE); | 251 gtk_widget_set_redraw_on_allocate(dangerous_prompt_, TRUE); |
256 g_signal_connect(dangerous_prompt_, "expose-event", | 252 g_signal_connect(dangerous_prompt_, "expose-event", |
257 G_CALLBACK(OnDangerousPromptExposeThunk), this); | 253 G_CALLBACK(OnDangerousPromptExposeThunk), this); |
258 gtk_widget_show_all(dangerous_prompt_); | 254 gtk_widget_show_all(dangerous_prompt_); |
259 } | 255 } |
260 | 256 |
261 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | 257 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, |
262 content::Source<ThemeService>(theme_service_)); | 258 content::Source<ThemeService>(theme_service_)); |
263 theme_service_->InitThemesFor(this); | 259 theme_service_->InitThemesFor(this); |
264 | 260 |
265 // Set the initial width of the widget to be animated. | 261 // Set the initial width of the widget to be animated. |
266 if (IsDangerous()) { | 262 if (download_model_->IsDangerous()) { |
267 gtk_widget_set_size_request(dangerous_hbox_.get(), | 263 gtk_widget_set_size_request(dangerous_hbox_.get(), |
268 dangerous_hbox_start_width_, -1); | 264 dangerous_hbox_start_width_, -1); |
269 } else { | 265 } else { |
270 gtk_widget_set_size_request(body_.get(), kMinDownloadItemWidth, -1); | 266 gtk_widget_set_size_request(body_.get(), kMinDownloadItemWidth, -1); |
271 } | 267 } |
272 | 268 |
273 new_item_animation_->Show(); | 269 new_item_animation_->Show(); |
274 | 270 |
275 complete_animation_.SetTweenType(ui::Tween::LINEAR); | 271 complete_animation_.SetTweenType(ui::Tween::LINEAR); |
276 complete_animation_.SetSlideDuration(kCompleteAnimationDurationMs); | 272 complete_animation_.SetSlideDuration(kCompleteAnimationDurationMs); |
(...skipping 17 matching lines...) Expand all Loading... |
294 dangerous_hbox_.Destroy(); | 290 dangerous_hbox_.Destroy(); |
295 | 291 |
296 // Make sure this widget has been destroyed and the pointer we hold to it | 292 // Make sure this widget has been destroyed and the pointer we hold to it |
297 // NULLed. | 293 // NULLed. |
298 DCHECK(!status_label_); | 294 DCHECK(!status_label_); |
299 } | 295 } |
300 | 296 |
301 void DownloadItemGtk::OnDownloadUpdated(DownloadItem* download) { | 297 void DownloadItemGtk::OnDownloadUpdated(DownloadItem* download) { |
302 DCHECK_EQ(download, get_download()); | 298 DCHECK_EQ(download, get_download()); |
303 | 299 |
304 if (dangerous_prompt_ != NULL && | 300 if (dangerous_prompt_ != NULL && !download_model_->IsDangerous()) { |
305 download->GetSafetyState() == DownloadItem::DANGEROUS_BUT_VALIDATED) { | |
306 // We have been approved. | 301 // We have been approved. |
307 gtk_widget_set_no_show_all(body_.get(), FALSE); | 302 gtk_widget_set_no_show_all(body_.get(), FALSE); |
308 gtk_widget_set_no_show_all(menu_button_, FALSE); | 303 gtk_widget_set_no_show_all(menu_button_, FALSE); |
309 gtk_widget_show_all(hbox_.get()); | 304 gtk_widget_show_all(hbox_.get()); |
310 gtk_widget_destroy(dangerous_prompt_); | 305 gtk_widget_destroy(dangerous_prompt_); |
311 gtk_widget_set_size_request(body_.get(), kBodyWidth, -1); | 306 gtk_widget_set_size_request(body_.get(), kBodyWidth, -1); |
312 dangerous_prompt_ = NULL; | 307 dangerous_prompt_ = NULL; |
313 | 308 |
314 // We may free some shelf space for showing more download items. | 309 // We may free some shelf space for showing more download items. |
315 parent_shelf_->MaybeShowMoreDownloadItems(); | 310 parent_shelf_->MaybeShowMoreDownloadItems(); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
368 | 363 |
369 status_text_ = UTF16ToUTF8(download_model_->GetStatusText()); | 364 status_text_ = UTF16ToUTF8(download_model_->GetStatusText()); |
370 UpdateStatusLabel(status_text_); | 365 UpdateStatusLabel(status_text_); |
371 } | 366 } |
372 | 367 |
373 void DownloadItemGtk::AnimationProgressed(const ui::Animation* animation) { | 368 void DownloadItemGtk::AnimationProgressed(const ui::Animation* animation) { |
374 if (animation == &complete_animation_) { | 369 if (animation == &complete_animation_) { |
375 gtk_widget_queue_draw(progress_area_.get()); | 370 gtk_widget_queue_draw(progress_area_.get()); |
376 } else { | 371 } else { |
377 DCHECK(animation == new_item_animation_.get()); | 372 DCHECK(animation == new_item_animation_.get()); |
378 if (IsDangerous()) { | 373 if (download_model_->IsDangerous()) { |
379 int progress = static_cast<int>((dangerous_hbox_full_width_ - | 374 int progress = static_cast<int>((dangerous_hbox_full_width_ - |
380 dangerous_hbox_start_width_) * | 375 dangerous_hbox_start_width_) * |
381 animation->GetCurrentValue()); | 376 animation->GetCurrentValue()); |
382 int showing_width = dangerous_hbox_start_width_ + progress; | 377 int showing_width = dangerous_hbox_start_width_ + progress; |
383 gtk_widget_set_size_request(dangerous_hbox_.get(), showing_width, -1); | 378 gtk_widget_set_size_request(dangerous_hbox_.get(), showing_width, -1); |
384 } else { | 379 } else { |
385 int showing_width = std::max(kMinDownloadItemWidth, | 380 int showing_width = std::max(kMinDownloadItemWidth, |
386 static_cast<int>(kBodyWidth * animation->GetCurrentValue())); | 381 static_cast<int>(kBodyWidth * animation->GetCurrentValue())); |
387 gtk_widget_set_size_request(body_.get(), showing_width, -1); | 382 gtk_widget_set_size_request(body_.get(), showing_width, -1); |
388 } | 383 } |
(...skipping 29 matching lines...) Expand all Loading... |
418 UpdateNameLabel(); | 413 UpdateNameLabel(); |
419 UpdateStatusLabel(status_text_); | 414 UpdateStatusLabel(status_text_); |
420 UpdateDangerWarning(); | 415 UpdateDangerWarning(); |
421 } | 416 } |
422 } | 417 } |
423 | 418 |
424 DownloadItem* DownloadItemGtk::get_download() { | 419 DownloadItem* DownloadItemGtk::get_download() { |
425 return download_model_->download(); | 420 return download_model_->download(); |
426 } | 421 } |
427 | 422 |
428 bool DownloadItemGtk::IsDangerous() { | |
429 return get_download()->GetSafetyState() == DownloadItem::DANGEROUS; | |
430 } | |
431 | |
432 // Download progress animation functions. | 423 // Download progress animation functions. |
433 | 424 |
434 void DownloadItemGtk::UpdateDownloadProgress() { | 425 void DownloadItemGtk::UpdateDownloadProgress() { |
435 progress_angle_ = (progress_angle_ + | 426 progress_angle_ = (progress_angle_ + |
436 download_util::kUnknownIncrementDegrees) % | 427 download_util::kUnknownIncrementDegrees) % |
437 download_util::kMaxDegrees; | 428 download_util::kMaxDegrees; |
438 gtk_widget_queue_draw(progress_area_.get()); | 429 gtk_widget_queue_draw(progress_area_.get()); |
439 } | 430 } |
440 | 431 |
441 void DownloadItemGtk::StartDownloadProgress() { | 432 void DownloadItemGtk::StartDownloadProgress() { |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
559 theme_service_->UsingNativeTheme() ? NULL : &text_color); | 550 theme_service_->UsingNativeTheme() ? NULL : &text_color); |
560 gtk_label_set_text(GTK_LABEL(status_label_), status_text.c_str()); | 551 gtk_label_set_text(GTK_LABEL(status_label_), status_text.c_str()); |
561 } | 552 } |
562 | 553 |
563 void DownloadItemGtk::UpdateDangerWarning() { | 554 void DownloadItemGtk::UpdateDangerWarning() { |
564 if (dangerous_prompt_) { | 555 if (dangerous_prompt_) { |
565 UpdateDangerIcon(); | 556 UpdateDangerIcon(); |
566 | 557 |
567 // We create |dangerous_warning| as a wide string so we can more easily | 558 // We create |dangerous_warning| as a wide string so we can more easily |
568 // calculate its length in characters. | 559 // calculate its length in characters. |
569 string16 dangerous_warning; | 560 string16 dangerous_warning = |
570 | 561 download_model_->GetWarningText(gfx::Font(), kTextWidth); |
571 // The dangerous download label text is different for different cases. | |
572 if (get_download()->GetDangerType() == | |
573 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL) { | |
574 // TODO(noelutz): handle malicious content warning. | |
575 // Safebrowsing shows the download URL leads to malicious file. | |
576 dangerous_warning = | |
577 l10n_util::GetStringUTF16(IDS_PROMPT_MALICIOUS_DOWNLOAD_URL); | |
578 } else { | |
579 // It's a dangerous file type (e.g.: an executable). | |
580 DCHECK(get_download()->GetDangerType() == | |
581 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE); | |
582 if (ChromeDownloadManagerDelegate::IsExtensionDownload(get_download())) { | |
583 dangerous_warning = | |
584 l10n_util::GetStringUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION); | |
585 } else { | |
586 string16 elided_filename = ui::ElideFilename( | |
587 get_download()->GetTargetName(), gfx::Font(), kTextWidth); | |
588 dangerous_warning = | |
589 l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD, | |
590 elided_filename); | |
591 } | |
592 } | |
593 | |
594 if (theme_service_->UsingNativeTheme()) { | 562 if (theme_service_->UsingNativeTheme()) { |
595 gtk_util::SetLabelColor(dangerous_label_, NULL); | 563 gtk_util::SetLabelColor(dangerous_label_, NULL); |
596 } else { | 564 } else { |
597 GdkColor color = theme_service_->GetGdkColor( | 565 GdkColor color = theme_service_->GetGdkColor( |
598 ThemeService::COLOR_BOOKMARK_TEXT); | 566 ThemeService::COLOR_BOOKMARK_TEXT); |
599 gtk_util::SetLabelColor(dangerous_label_, &color); | 567 gtk_util::SetLabelColor(dangerous_label_, &color); |
600 } | 568 } |
601 | 569 |
602 gtk_label_set_text(GTK_LABEL(dangerous_label_), | 570 gtk_label_set_text(GTK_LABEL(dangerous_label_), |
603 UTF16ToUTF8(dangerous_warning).c_str()); | 571 UTF16ToUTF8(dangerous_warning).c_str()); |
(...skipping 26 matching lines...) Expand all Loading... |
630 // The width will depend on the text. We must do this each time we possibly | 598 // The width will depend on the text. We must do this each time we possibly |
631 // change the label above. | 599 // change the label above. |
632 gtk_widget_size_request(dangerous_hbox_.get(), &req); | 600 gtk_widget_size_request(dangerous_hbox_.get(), &req); |
633 dangerous_hbox_full_width_ = req.width; | 601 dangerous_hbox_full_width_ = req.width; |
634 dangerous_hbox_start_width_ = dangerous_hbox_full_width_ - label_width; | 602 dangerous_hbox_start_width_ = dangerous_hbox_full_width_ - label_width; |
635 } | 603 } |
636 } | 604 } |
637 | 605 |
638 void DownloadItemGtk::UpdateDangerIcon() { | 606 void DownloadItemGtk::UpdateDangerIcon() { |
639 if (theme_service_->UsingNativeTheme()) { | 607 if (theme_service_->UsingNativeTheme()) { |
640 const char* stock = get_download()->GetDangerType() == | 608 const char* stock = download_model_->IsMalicious() ? |
641 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ? | 609 GTK_STOCK_DIALOG_ERROR : GTK_STOCK_DIALOG_WARNING; |
642 GTK_STOCK_DIALOG_ERROR : GTK_STOCK_DIALOG_WARNING; | |
643 gtk_image_set_from_stock( | 610 gtk_image_set_from_stock( |
644 GTK_IMAGE(dangerous_image_), stock, GTK_ICON_SIZE_SMALL_TOOLBAR); | 611 GTK_IMAGE(dangerous_image_), stock, GTK_ICON_SIZE_SMALL_TOOLBAR); |
645 } else { | 612 } else { |
646 // Set the warning icon. | 613 // Set the warning icon. |
647 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 614 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
648 int pixbuf_id = get_download()->GetDangerType() == | 615 int pixbuf_id = download_model_->IsMalicious() ? |
649 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ? | 616 IDR_SAFEBROWSING_WARNING : IDR_WARNING; |
650 IDR_SAFEBROWSING_WARNING : IDR_WARNING; | |
651 GdkPixbuf* download_pixbuf = rb.GetNativeImageNamed(pixbuf_id); | 617 GdkPixbuf* download_pixbuf = rb.GetNativeImageNamed(pixbuf_id); |
652 gtk_image_set_from_pixbuf(GTK_IMAGE(dangerous_image_), download_pixbuf); | 618 gtk_image_set_from_pixbuf(GTK_IMAGE(dangerous_image_), download_pixbuf); |
653 } | 619 } |
654 } | 620 } |
655 | 621 |
656 // static | 622 // static |
657 void DownloadItemGtk::InitNineBoxes() { | 623 void DownloadItemGtk::InitNineBoxes() { |
658 if (body_nine_box_normal_) | 624 if (body_nine_box_normal_) |
659 return; | 625 return; |
660 | 626 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
722 TRACE_EVENT0("ui::gtk", "DownloadItemGtk::OnHboxExpose"); | 688 TRACE_EVENT0("ui::gtk", "DownloadItemGtk::OnHboxExpose"); |
723 if (theme_service_->UsingNativeTheme()) { | 689 if (theme_service_->UsingNativeTheme()) { |
724 GtkAllocation allocation; | 690 GtkAllocation allocation; |
725 gtk_widget_get_allocation(widget, &allocation); | 691 gtk_widget_get_allocation(widget, &allocation); |
726 int border_width = gtk_container_get_border_width(GTK_CONTAINER(widget)); | 692 int border_width = gtk_container_get_border_width(GTK_CONTAINER(widget)); |
727 int x = allocation.x + border_width; | 693 int x = allocation.x + border_width; |
728 int y = allocation.y + border_width; | 694 int y = allocation.y + border_width; |
729 int width = allocation.width - border_width * 2; | 695 int width = allocation.width - border_width * 2; |
730 int height = allocation.height - border_width * 2; | 696 int height = allocation.height - border_width * 2; |
731 | 697 |
732 if (IsDangerous()) { | 698 if (download_model_->IsDangerous()) { |
733 // Draw a simple frame around the area when we're displaying the warning. | 699 // Draw a simple frame around the area when we're displaying the warning. |
734 gtk_paint_shadow(gtk_widget_get_style(widget), | 700 gtk_paint_shadow(gtk_widget_get_style(widget), |
735 gtk_widget_get_window(widget), | 701 gtk_widget_get_window(widget), |
736 gtk_widget_get_state(widget), | 702 gtk_widget_get_state(widget), |
737 static_cast<GtkShadowType>(GTK_SHADOW_OUT), | 703 static_cast<GtkShadowType>(GTK_SHADOW_OUT), |
738 &e->area, widget, "frame", | 704 &e->area, widget, "frame", |
739 x, y, width, height); | 705 x, y, width, height); |
740 } else { | 706 } else { |
741 // Manually draw the GTK button border around the download item. We draw | 707 // Manually draw the GTK button border around the download item. We draw |
742 // the left part of the button (the file), a divider, and then the right | 708 // the left part of the button (the file), a divider, and then the right |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
914 get_download()->DangerousDownloadValidated(); | 880 get_download()->DangerousDownloadValidated(); |
915 } | 881 } |
916 | 882 |
917 void DownloadItemGtk::OnDangerousDecline(GtkWidget* button) { | 883 void DownloadItemGtk::OnDangerousDecline(GtkWidget* button) { |
918 UMA_HISTOGRAM_LONG_TIMES("clickjacking.discard_download", | 884 UMA_HISTOGRAM_LONG_TIMES("clickjacking.discard_download", |
919 base::Time::Now() - creation_time_); | 885 base::Time::Now() - creation_time_); |
920 if (get_download()->IsPartialDownload()) | 886 if (get_download()->IsPartialDownload()) |
921 get_download()->Cancel(true); | 887 get_download()->Cancel(true); |
922 get_download()->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD); | 888 get_download()->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD); |
923 } | 889 } |
OLD | NEW |