diff options
author | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-10 18:50:32 +0000 |
---|---|---|
committer | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-10 18:50:32 +0000 |
commit | 9ccbb370aa45f477941e0599d4ce7c89fac64101 (patch) | |
tree | 8b21818fa95d05ff00dfe5b9986d1d39eb4be722 | |
parent | a9acde54584ff37bcc5fad73cf25dce5d85348bc (diff) | |
download | chromium_src-9ccbb370aa45f477941e0599d4ce7c89fac64101.zip chromium_src-9ccbb370aa45f477941e0599d4ce7c89fac64101.tar.gz chromium_src-9ccbb370aa45f477941e0599d4ce7c89fac64101.tar.bz2 |
This CL adds prompting for dangerous types of files (executable) when they are automatically downloaded.
The file is saved with a temporary name (dangerous_download_xxxx.download) in the download directory and the user is presented (in the download shelf and the download tab if opened) with a warning message and buttons to save/discard the download.
If discarded the download is removed (and its file deleted).
If saved, download goes as usual.
Dangerous downloads not confirmed by the user are deleted on shutdown.
TEST=Download a small exe file, try using the save/discard button from the download shelf and from the download tab (the intent is that the file has been entirely downloaded by the time you take action). Try again with a slow/big download (that time the download is expected not to be finished when approved/discarded).
Review URL: http://codereview.chromium.org/6043
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3228 0039d316-1c4b-4281-b951-d872f2087c98
33 files changed, 1334 insertions, 436 deletions
diff --git a/base/file_util.h b/base/file_util.h index 9624e60..150b629 100644 --- a/base/file_util.h +++ b/base/file_util.h @@ -224,6 +224,10 @@ bool GetTempDir(std::wstring* path); // TODO(erikkay): rename this function and track down all of the callers. bool CreateTemporaryFileName(std::wstring* temp_file); +// Same as CreateTemporaryFileName but the file is created in |dir|. +bool CreateTemporaryFileNameInDir(const std::wstring& dir, + std::wstring* temp_file); + // Create a new directory under TempPath. If prefix is provided, the new // directory name is in the format of prefixyyyy. // If success, return true and output the full path of the directory created. diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc index edb20c7..e440640 100644 --- a/base/file_util_posix.cc +++ b/base/file_util_posix.cc @@ -267,6 +267,13 @@ bool CreateTemporaryFileName(std::wstring* temp_file) { return true; } +bool CreateTemporaryFileNameInDir(const std::wstring& dir, + std::wstring* temp_file) { + // Not implemented yet. + NOTREACHED(); + return false; +} + bool CreateNewTempDirectory(const std::wstring& prefix, std::wstring* new_temp_path) { std::wstring tmpdir; diff --git a/base/file_util_win.cc b/base/file_util_win.cc index 8253e53..7be172f 100644 --- a/base/file_util_win.cc +++ b/base/file_util_win.cc @@ -383,13 +383,19 @@ bool GetTempDir(std::wstring* path) { } bool CreateTemporaryFileName(std::wstring* temp_file) { - wchar_t temp_name[MAX_PATH + 1]; std::wstring temp_path; if (!GetTempDir(&temp_path)) return false; - if (!GetTempFileName(temp_path.c_str(), L"", 0, temp_name)) + return CreateTemporaryFileNameInDir(temp_path, temp_file); +} + +bool CreateTemporaryFileNameInDir(const std::wstring& dir, + std::wstring* temp_file) { + wchar_t temp_name[MAX_PATH + 1]; + + if (!GetTempFileName(dir.c_str(), L"", 0, temp_name)) return false; // fail! DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH); diff --git a/base/string_util.cc b/base/string_util.cc index 223c485..5fa2f75 100644 --- a/base/string_util.cc +++ b/base/string_util.cc @@ -1434,3 +1434,39 @@ size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { return lcpyT<wchar_t>(dst, src, dst_size); } +bool ElideString(const std::wstring& input, int max_len, std::wstring* output) { + DCHECK(max_len >= 0); + if (static_cast<int>(input.length()) <= max_len) { + output->assign(input); + return false; + } + + switch (max_len) { + case 0: + output->clear(); + break; + case 1: + output->assign(input.substr(0, 1)); + break; + case 2: + output->assign(input.substr(0, 2)); + break; + case 3: + output->assign(input.substr(0, 1) + L"." + + input.substr(input.length() - 1)); + break; + case 4: + output->assign(input.substr(0, 1) + L".." + + input.substr(input.length() - 1)); + break; + default: { + int rstr_len = (max_len - 3) / 2; + int lstr_len = rstr_len + ((max_len - 3) % 2); + output->assign(input.substr(0, lstr_len) + L"..." + + input.substr(input.length() - rstr_len)); + break; + } + } + + return true; +} diff --git a/base/string_util.h b/base/string_util.h index a9f08c4..981cd30 100644 --- a/base/string_util.h +++ b/base/string_util.h @@ -508,6 +508,14 @@ std::wstring ReplaceStringPlaceholders(const std::wstring& format_string, const std::wstring& d, std::vector<size_t>* offsets); +// If the size of |input| is more than |max_len|, this function returns true and +// |input| is shortened into |output| by removing chars in the middle (they are +// replaced with up to 3 dots, as size permits). +// Ex: ElideString(L"Hello", 10, &str) puts Hello in str and returns false. +// ElideString(L"Hello my name is Tom", 10, &str) puts "Hell...Tom" in str and +// returns true. +bool ElideString(const std::wstring& input, int max_len, std::wstring* output); + // Returns true if the string passed in matches the pattern. The pattern // string can contain wildcards like * and ? // TODO(iyengar) This function may not work correctly for CJK strings as diff --git a/base/string_util_unittest.cc b/base/string_util_unittest.cc index e438ebb..25f43c6 100644 --- a/base/string_util_unittest.cc +++ b/base/string_util_unittest.cc @@ -1380,3 +1380,29 @@ TEST(StringUtilTest, WprintfFormatPortabilityTest) { } } +TEST(StringUtilTest, ElideString) { + struct TestData { + const wchar_t* input; + int max_len; + bool result; + const wchar_t* output; + } cases[] = { + { L"Hello", 0, true, L"" }, + { L"", 0, false, L"" }, + { L"Hello, my name is Tom", 1, true, L"H" }, + { L"Hello, my name is Tom", 2, true, L"He" }, + { L"Hello, my name is Tom", 3, true, L"H.m" }, + { L"Hello, my name is Tom", 4, true, L"H..m" }, + { L"Hello, my name is Tom", 5, true, L"H...m" }, + { L"Hello, my name is Tom", 6, true, L"He...m" }, + { L"Hello, my name is Tom", 7, true, L"He...om" }, + { L"Hello, my name is Tom", 10, true, L"Hell...Tom" }, + { L"Hello, my name is Tom", 100, false, L"Hello, my name is Tom" } + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + std::wstring output; + EXPECT_EQ(cases[i].result, + ElideString(cases[i].input, cases[i].max_len, &output)); + EXPECT_TRUE(output == cases[i].output); + } +} diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 4ac4e2c..d9a3c38 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -1145,6 +1145,19 @@ each locale. --> Cancel </message> + <message name="IDS_PROMPT_DANGEROUS_DOWNLOAD" + desc="Message shown to the user to validate the download of a dangerous file."> + This type of file can harm your computer. Are you sure you want to download <ph name="FILE_NAME">$1<ex>malware.exe</ex></ph>? + </message> + <message name="IDS_SAVE_DOWNLOAD" + desc="Text for the button used to validate the downloading of a dangerous download."> + Save + </message> + <message name="IDS_DISCARD_DOWNLOAD" + desc="Text for the button used to stop a dangerous download."> + Discard + </message> + <!-- Download Tab Items --> <message name="IDS_DOWNLOAD_LINK_PAUSE" desc="In the download view, 'Pause' link text"> diff --git a/chrome/app/theme/download_button_right_bottom_no_dd.png b/chrome/app/theme/download_button_right_bottom_no_dd.png Binary files differnew file mode 100644 index 0000000..4c9d3db --- /dev/null +++ b/chrome/app/theme/download_button_right_bottom_no_dd.png diff --git a/chrome/app/theme/download_button_right_middle_no_dd.png b/chrome/app/theme/download_button_right_middle_no_dd.png Binary files differnew file mode 100644 index 0000000..df2cf9e --- /dev/null +++ b/chrome/app/theme/download_button_right_middle_no_dd.png diff --git a/chrome/app/theme/download_button_right_top_no_dd.png b/chrome/app/theme/download_button_right_top_no_dd.png Binary files differnew file mode 100644 index 0000000..f57fd85 --- /dev/null +++ b/chrome/app/theme/download_button_right_top_no_dd.png diff --git a/chrome/app/theme/theme_resources.h b/chrome/app/theme/theme_resources.h index 242bf45..a4cba37 100644 --- a/chrome/app/theme/theme_resources.h +++ b/chrome/app/theme/theme_resources.h @@ -12,303 +12,305 @@ #define IDR_CLOSE 9004 #define IDR_CLOSE_H 9005 #define IDR_CLOSE_P 9006 -#define IDR_CONTENT_BOTTOM_CENTER 9009 -#define IDR_CONTENT_BOTTOM_LEFT_CORNER 9010 -#define IDR_CONTENT_BOTTOM_RIGHT_CORNER 9011 -#define IDR_CONTENT_LEFT_SIDE 9012 -#define IDR_CONTENT_RIGHT_SIDE 9013 -#define IDR_CONTENT_TOP_CENTER 9014 -#define IDR_CONTENT_TOP_LEFT_CORNER 9015 -#define IDR_CONTENT_TOP_RIGHT_CORNER 9016 -#define IDR_DEFAULT_FAVICON 9017 -#define IDR_DROP 9018 -#define IDR_DROP_H 9019 -#define IDR_DROP_P 9020 -#define IDR_FORWARD 9021 -#define IDR_FORWARD_D 9022 -#define IDR_FORWARD_H 9023 -#define IDR_FORWARD_P 9024 -#define IDR_GO 9025 -#define IDR_GO_H 9026 -#define IDR_GO_P 9027 -#define IDR_INFO_BUBBLE_CLOSE 9028 -#define IDR_LOCATIONBG 9029 -#define IDR_MAXIMIZE 9030 -#define IDR_MAXIMIZE_H 9031 -#define IDR_MAXIMIZE_P 9032 -#define IDR_MENUITEM_CENTER_A 9033 -#define IDR_MENUITEM_CENTER_H 9034 -#define IDR_MENUITEM_LEFT_A 9035 -#define IDR_MENUITEM_LEFT_H 9036 -#define IDR_MENUITEM_RIGHT_A 9037 -#define IDR_MENUITEM_RIGHT_H 9038 -#define IDR_MINIMIZE 9039 -#define IDR_MINIMIZE_H 9040 -#define IDR_MINIMIZE_P 9041 -#define IDR_PLUGIN 9042 -#define IDR_RELOAD 9043 -#define IDR_RELOAD_H 9044 -#define IDR_RELOAD_P 9045 -#define IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_C 9046 -#define IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_L 9047 -#define IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_R 9048 -#define IDR_STAR 9076 -#define IDR_STAR_D 9077 -#define IDR_STAR_H 9078 -#define IDR_STAR_P 9079 -#define IDR_STARRED 9080 -#define IDR_STARRED_H 9081 -#define IDR_STARRED_P 9082 -#define IDR_TAB_ACTIVE_CENTER 9086 -#define IDR_TAB_ACTIVE_LEFT 9087 -#define IDR_TAB_ACTIVE_RIGHT 9088 -#define IDR_TAB_CLOSE 9089 -#define IDR_TAB_CLOSE_H 9090 -#define IDR_TAB_CLOSE_P 9091 -#define IDR_TAB_INACTIVE_CENTER 9092 -#define IDR_TAB_INACTIVE_LEFT 9093 -#define IDR_TAB_INACTIVE_RIGHT 9094 -#define IDR_THROBBER 9095 -#define IDR_TOOLBAR_TOGGLE_KNOB 9096 -#define IDR_TOOLBAR_TOGGLE_TRACK 9097 -#define IDR_WINDOW_BOTTOM_CENTER 9098 -#define IDR_WINDOW_BOTTOM_LEFT_CORNER 9099 -#define IDR_WINDOW_BOTTOM_RIGHT_CORNER 9100 -#define IDR_WINDOW_LEFT_SIDE 9101 -#define IDR_WINDOW_RIGHT_SIDE 9102 -#define IDR_WINDOW_TOP_CENTER 9103 -#define IDR_WINDOW_TOP_LEFT_CORNER 9104 -#define IDR_WINDOW_TOP_RIGHT_CORNER 9105 -#define IDR_WINDOW_BOTTOM_CENTER_OTR 9106 -#define IDR_WINDOW_BOTTOM_LEFT_CORNER_OTR 9107 -#define IDR_WINDOW_BOTTOM_RIGHT_CORNER_OTR 9108 -#define IDR_WINDOW_LEFT_SIDE_OTR 9109 -#define IDR_WINDOW_RIGHT_SIDE_OTR 9110 -#define IDR_WINDOW_TOP_CENTER_OTR 9111 -#define IDR_WINDOW_TOP_LEFT_CORNER_OTR 9112 -#define IDR_WINDOW_TOP_RIGHT_CORNER_OTR 9113 -#define IDR_TAB_INACTIVE_CENTER_V 9114 -#define IDR_TAB_INACTIVE_LEFT_V 9115 -#define IDR_TAB_INACTIVE_RIGHT_V 9116 -#define IDR_RESTORE 9117 -#define IDR_RESTORE_H 9118 -#define IDR_RESTORE_P 9119 -#define IDR_DEWINDOW_BOTTOM_CENTER 9120 -#define IDR_DEWINDOW_BOTTOM_LEFT_CORNER 9121 -#define IDR_DEWINDOW_BOTTOM_RIGHT_CORNER 9122 -#define IDR_DEWINDOW_LEFT_SIDE 9123 -#define IDR_DEWINDOW_RIGHT_SIDE 9124 -#define IDR_DEWINDOW_TOP_CENTER 9125 -#define IDR_DEWINDOW_TOP_LEFT_CORNER 9126 -#define IDR_DEWINDOW_TOP_RIGHT_CORNER 9127 -#define IDR_DEWINDOW_BOTTOM_CENTER_OTR 9128 -#define IDR_DEWINDOW_BOTTOM_LEFT_CORNER_OTR 9129 -#define IDR_DEWINDOW_BOTTOM_RIGHT_CORNER_OTR 9130 -#define IDR_DEWINDOW_LEFT_SIDE_OTR 9131 -#define IDR_DEWINDOW_RIGHT_SIDE_OTR 9132 -#define IDR_DEWINDOW_TOP_CENTER_OTR 9133 -#define IDR_DEWINDOW_TOP_LEFT_CORNER_OTR 9134 -#define IDR_DEWINDOW_TOP_RIGHT_CORNER_OTR 9135 -#define IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM 9137 -#define IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_H 9138 -#define IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_P 9139 -#define IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE 9140 -#define IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_H 9141 -#define IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_P 9142 -#define IDR_DOWNLOAD_BUTTON_CENTER_TOP 9143 -#define IDR_DOWNLOAD_BUTTON_CENTER_TOP_H 9144 -#define IDR_DOWNLOAD_BUTTON_CENTER_TOP_P 9145 -#define IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM 9146 -#define IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_H 9147 -#define IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_P 9148 -#define IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE 9149 -#define IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_H 9150 -#define IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_P 9151 -#define IDR_DOWNLOAD_BUTTON_LEFT_TOP 9152 -#define IDR_DOWNLOAD_BUTTON_LEFT_TOP_H 9153 -#define IDR_DOWNLOAD_BUTTON_LEFT_TOP_P 9154 -#define IDR_DOWNLOAD_BUTTON_MENU_BOTTOM 9155 -#define IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_H 9156 -#define IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_P 9157 -#define IDR_DOWNLOAD_BUTTON_MENU_MIDDLE 9158 -#define IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_H 9159 -#define IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_P 9160 -#define IDR_DOWNLOAD_BUTTON_MENU_TOP 9161 -#define IDR_DOWNLOAD_BUTTON_MENU_TOP_H 9162 -#define IDR_DOWNLOAD_BUTTON_MENU_TOP_P 9163 -#define IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM 9164 -#define IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_H 9165 -#define IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_P 9166 -#define IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE 9167 -#define IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_H 9168 -#define IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_P 9169 -#define IDR_DOWNLOAD_BUTTON_RIGHT_TOP 9170 -#define IDR_DOWNLOAD_BUTTON_RIGHT_TOP_H 9171 -#define IDR_DOWNLOAD_BUTTON_RIGHT_TOP_P 9172 -#define IDR_DOWNLOAD_PROGRESS_BACKGROUND_16 9173 -#define IDR_DOWNLOAD_PROGRESS_FOREGROUND_16 9174 -#define IDR_DOWNLOAD_PROGRESS_BACKGROUND_32 9175 -#define IDR_DOWNLOAD_PROGRESS_FOREGROUND_32 9176 -#define IDR_DOWNLOAD_ICON 9177 -#define IDR_COOKIE_ICON 9178 -#define IDR_SAD_FAVICON 9179 -#define IDR_TAB_DROP_UP 9180 -#define IDR_TAB_DROP_DOWN 9181 -#define IDR_CONSTRAINED_BOTTOM_CENTER 9182 -#define IDR_CONSTRAINED_BOTTOM_LEFT_CORNER 9183 -#define IDR_CONSTRAINED_BOTTOM_RIGHT_CORNER 9184 -#define IDR_CONSTRAINED_LEFT_SIDE 9185 -#define IDR_CONSTRAINED_RIGHT_SIDE 9186 -#define IDR_CONSTRAINED_TOP_CENTER 9187 -#define IDR_CONSTRAINED_TOP_LEFT_CORNER 9188 -#define IDR_CONSTRAINED_TOP_RIGHT_CORNER 9189 -#define IDR_FIND_BOX_BACKGROUND 9190 -#define IDR_FIND_BOX_BACKGROUND_LEFT 9191 -#define IDR_LOCK 9192 -#define IDR_WARNING 9193 -#define IDR_BOOKMARK_BAR_RECENTLY_BOOKMARKED_ICON 9195 -#define IDR_STOP 9197 -#define IDR_STOP_H 9198 -#define IDR_STOP_P 9199 -#define IDR_FIND_DLG_LEFT_BACKGROUND 9200 -#define IDR_FIND_DLG_RIGHT_BACKGROUND 9201 -#define IDR_FIND_DLG_MIDDLE_BACKGROUND 9202 -#define IDR_APP_TOP_RIGHT 9203 -#define IDR_APP_TOP_CENTER 9204 -#define IDR_APP_TOP_LEFT 9205 -#define IDR_APP_DROPARROW 9206 -#define IDR_THROBBER_01 9207 -#define IDR_THROBBER_02 9208 -#define IDR_THROBBER_03 9209 -#define IDR_THROBBER_04 9210 -#define IDR_THROBBER_05 9211 -#define IDR_THROBBER_06 9212 -#define IDR_THROBBER_07 9213 -#define IDR_THROBBER_08 9214 -#define IDR_THROBBER_09 9215 -#define IDR_THROBBER_10 9216 -#define IDR_THROBBER_11 9217 -#define IDR_THROBBER_12 9218 -#define IDR_THROBBER_13 9219 -#define IDR_THROBBER_14 9220 -#define IDR_THROBBER_15 9221 -#define IDR_THROBBER_16 9222 -#define IDR_THROBBER_17 9223 -#define IDR_THROBBER_18 9224 -#define IDR_THROBBER_19 9225 -#define IDR_THROBBER_20 9226 -#define IDR_THROBBER_21 9227 -#define IDR_THROBBER_22 9228 -#define IDR_THROBBER_23 9229 -#define IDR_THROBBER_24 9230 -#define IDR_PAGEINFO_GOOD 9231 -#define IDR_PAGEINFO_BAD 9232 -#define IDR_NEWTAB_BUTTON 9233 -#define IDR_NEWTAB_BUTTON_H 9234 -#define IDR_NEWTAB_BUTTON_P 9235 -#define IDR_ARROW_RIGHT 9236 -#define IDR_TEXTBUTTON_TOP_LEFT_H 9270 -#define IDR_TEXTBUTTON_TOP_H 9271 -#define IDR_TEXTBUTTON_TOP_RIGHT_H 9272 -#define IDR_TEXTBUTTON_LEFT_H 9273 -#define IDR_TEXTBUTTON_CENTER_H 9274 -#define IDR_TEXTBUTTON_RIGHT_H 9275 -#define IDR_TEXTBUTTON_BOTTOM_LEFT_H 9276 -#define IDR_TEXTBUTTON_BOTTOM_H 9277 -#define IDR_TEXTBUTTON_BOTTOM_RIGHT_H 9278 -#define IDR_TEXTBUTTON_TOP_LEFT_P 9279 -#define IDR_TEXTBUTTON_TOP_P 9280 -#define IDR_TEXTBUTTON_TOP_RIGHT_P 9281 -#define IDR_TEXTBUTTON_LEFT_P 9282 -#define IDR_TEXTBUTTON_CENTER_P 9283 -#define IDR_TEXTBUTTON_RIGHT_P 9284 -#define IDR_TEXTBUTTON_BOTTOM_LEFT_P 9285 -#define IDR_TEXTBUTTON_BOTTOM_P 9286 -#define IDR_TEXTBUTTON_BOTTOM_RIGHT_P 9287 -#define IDR_SAD_TAB 9288 -#define IDR_FOLDER_OPEN 9289 -#define IDR_FOLDER_CLOSED 9290 -#define IDR_OTR_ICON 9291 -#define IDR_TAB_INACTIVE_LEFT_OTR 9292 -#define IDR_TAB_INACTIVE_CENTER_OTR 9293 -#define IDR_TAB_INACTIVE_RIGHT_OTR 9294 -#define IDR_BOOKMARK_BAR_CHEVRONS 9295 -#define IDR_CONSTRAINED_BOTTOM_CENTER_V 9296 -#define IDR_CONSTRAINED_BOTTOM_LEFT_CORNER_V 9297 -#define IDR_CONSTRAINED_BOTTOM_RIGHT_CORNER_V 9298 -#define IDR_CONSTRAINED_LEFT_SIDE_V 9299 -#define IDR_CONSTRAINED_RIGHT_SIDE_V 9300 -#define IDR_CONSTRAINED_TOP_CENTER_V 9301 -#define IDR_CONSTRAINED_TOP_LEFT_CORNER_V 9302 -#define IDR_CONSTRAINED_TOP_RIGHT_CORNER_V 9303 -#define IDR_CONTENT_STAR_D 9304 -#define IDR_CONTENT_STAR_OFF 9305 -#define IDR_CONTENT_STAR_ON 9306 -#define IDR_LOCATION_BAR_KEYWORD_HINT_TAB 9307 -#define IDR_ABOUT_BACKGROUND 9310 -#define IDR_FINDINPAGE_PREV 9312 -#define IDR_FINDINPAGE_PREV_H 9313 -#define IDR_FINDINPAGE_PREV_P 9314 -#define IDR_FINDINPAGE_NEXT 9315 -#define IDR_FINDINPAGE_NEXT_H 9316 -#define IDR_FINDINPAGE_NEXT_P 9317 -#define IDR_INFOBAR_RESTORE_SESSION 9322 -#define IDR_INFOBAR_SAVE_PASSWORD 9323 -#define IDR_INFOBAR_SSL_WARNING 9324 -#define IDR_INFOBAR_ALT_NAV_URL 9325 -#define IDR_INFOBAR_PLUGIN_INSTALL 9326 -#define IDR_INFO_BUBBLE_CORNER_TOP_LEFT 9327 -#define IDR_INFO_BUBBLE_CORNER_TOP_RIGHT 9328 -#define IDR_INFO_BUBBLE_CORNER_BOTTOM_LEFT 9329 -#define IDR_INFO_BUBBLE_CORNER_BOTTOM_RIGHT 9330 -#define IDR_WIZARD_ICON 9331 -#define IDR_MENU_MARKER 9332 -#define IDR_FROZEN_TAB_ICON 9333 -#define IDR_FROZEN_PLUGIN_ICON 9334 -#define IDR_UPDATE_AVAILABLE 9335 -#define IDR_MENU_PAGE 9336 -#define IDR_MENU_CHROME 9337 -#define IDR_ABOUT_BACKGROUND_RTL 9339 -#define IDR_WIZARD_ICON_RTL 9340 -#define IDR_LOCATIONBG_POPUPMODE_LEFT 9341 -#define IDR_LOCATIONBG_POPUPMODE_CENTER 9342 -#define IDR_LOCATIONBG_POPUPMODE_RIGHT 9343 -#define IDR_CLOSE_SA 9344 -#define IDR_CLOSE_SA_H 9345 -#define IDR_CLOSE_SA_P 9346 -#define IDR_HISTORY_SECTION 9347 -#define IDR_DOWNLOADS_SECTION 9348 -#define IDR_DEFAULT_THUMBNAIL 9349 -#define IDR_THROBBER_WAITING 9350 -#define IDR_INFOBAR_PLUGIN_CRASHED 9351 -#define IDR_UPDATE_UPTODATE 9353 -#define IDR_UPDATE_FAIL 9354 -#define IDR_CLOSE_BAR 9355 -#define IDR_CLOSE_BAR_H 9356 -#define IDR_CLOSE_BAR_P 9357 -#define IDR_HOME 9358 -#define IDR_HOME_H 9359 -#define IDR_HOME_P 9360 -#define IDR_FIND_BOX_BACKGROUND_LEFT_RTL 9361 -#define IDR_INPUT_GOOD 9362 -// 9363 is unused. -#define IDR_INPUT_ALERT 9364 -#define IDR_HISTORY_FAVICON 9365 -#define IDR_DOWNLOADS_FAVICON 9366 -#define IDR_MENU_PAGE_RTL 9367 -#define IDR_MENU_CHROME_RTL 9368 -#define IDR_DOWNLOAD_ANIMATION_BEGIN 9369 -#define IDR_TAB_HOVER_LEFT 9370 -#define IDR_TAB_HOVER_CENTER 9371 -#define IDR_TAB_HOVER_RIGHT 9372 -#define IDR_FOLDER_CLOSED_RTL 9373 -#define IDR_FOLDER_OPEN_RTL 9374 -#define IDR_BOOKMARK_BAR_FOLDER 9375 -#define IDR_FIND_DLG_LEFT_BB_BACKGROUND 9376 -#define IDR_FIND_DLG_RIGHT_BB_BACKGROUND 9377 -#define IDR_FIND_DLG_MIDDLE_BB_BACKGROUND 9378 -#define IDR_THROBBER_LIGHT 9379 -#define IDR_OTR_ICON_STANDALONE 9380 -#define IDR_PRODUCT_LOGO 9381 -#define IDR_DISTRIBUTOR_LOGO 9382 -#define IDR_DISTRIBUTOR_LOGO_LIGHT 9383 +#define IDR_CONTENT_BOTTOM_CENTER 9007 +#define IDR_CONTENT_BOTTOM_LEFT_CORNER 9008 +#define IDR_CONTENT_BOTTOM_RIGHT_CORNER 9009 +#define IDR_CONTENT_LEFT_SIDE 9010 +#define IDR_CONTENT_RIGHT_SIDE 9011 +#define IDR_CONTENT_TOP_CENTER 9012 +#define IDR_CONTENT_TOP_LEFT_CORNER 9013 +#define IDR_CONTENT_TOP_RIGHT_CORNER 9014 +#define IDR_DEFAULT_FAVICON 9015 +#define IDR_DROP 9016 +#define IDR_DROP_H 9017 +#define IDR_DROP_P 9018 +#define IDR_FORWARD 9019 +#define IDR_FORWARD_D 9020 +#define IDR_FORWARD_H 9021 +#define IDR_FORWARD_P 9022 +#define IDR_GO 9023 +#define IDR_GO_H 9024 +#define IDR_GO_P 9025 +#define IDR_INFO_BUBBLE_CLOSE 9026 +#define IDR_LOCATIONBG 9027 +#define IDR_MAXIMIZE 9028 +#define IDR_MAXIMIZE_H 9029 +#define IDR_MAXIMIZE_P 9030 +#define IDR_MENUITEM_CENTER_A 9031 +#define IDR_MENUITEM_CENTER_H 9032 +#define IDR_MENUITEM_LEFT_A 9033 +#define IDR_MENUITEM_LEFT_H 9034 +#define IDR_MENUITEM_RIGHT_A 9035 +#define IDR_MENUITEM_RIGHT_H 9036 +#define IDR_MINIMIZE 9037 +#define IDR_MINIMIZE_H 9038 +#define IDR_MINIMIZE_P 9039 +#define IDR_PLUGIN 9040 +#define IDR_RELOAD 9041 +#define IDR_RELOAD_H 9042 +#define IDR_RELOAD_P 9043 +#define IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_C 9044 +#define IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_L 9045 +#define IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_R 9046 +#define IDR_STAR 9047 +#define IDR_STAR_D 9048 +#define IDR_STAR_H 9049 +#define IDR_STAR_P 9050 +#define IDR_STARRED 9051 +#define IDR_STARRED_H 9052 +#define IDR_STARRED_P 9053 +#define IDR_TAB_ACTIVE_CENTER 9054 +#define IDR_TAB_ACTIVE_LEFT 9055 +#define IDR_TAB_ACTIVE_RIGHT 9056 +#define IDR_TAB_CLOSE 9057 +#define IDR_TAB_CLOSE_H 9058 +#define IDR_TAB_CLOSE_P 9059 +#define IDR_TAB_INACTIVE_CENTER 9060 +#define IDR_TAB_INACTIVE_LEFT 9061 +#define IDR_TAB_INACTIVE_RIGHT 9062 +#define IDR_THROBBER 9063 +#define IDR_TOOLBAR_TOGGLE_KNOB 9064 +#define IDR_TOOLBAR_TOGGLE_TRACK 9065 +#define IDR_WINDOW_BOTTOM_CENTER 9066 +#define IDR_WINDOW_BOTTOM_LEFT_CORNER 9067 +#define IDR_WINDOW_BOTTOM_RIGHT_CORNER 9068 +#define IDR_WINDOW_LEFT_SIDE 9069 +#define IDR_WINDOW_RIGHT_SIDE 9070 +#define IDR_WINDOW_TOP_CENTER 9071 +#define IDR_WINDOW_TOP_LEFT_CORNER 9072 +#define IDR_WINDOW_TOP_RIGHT_CORNER 9073 +#define IDR_WINDOW_BOTTOM_CENTER_OTR 9074 +#define IDR_WINDOW_BOTTOM_LEFT_CORNER_OTR 9075 +#define IDR_WINDOW_BOTTOM_RIGHT_CORNER_OTR 9076 +#define IDR_WINDOW_LEFT_SIDE_OTR 9077 +#define IDR_WINDOW_RIGHT_SIDE_OTR 9078 +#define IDR_WINDOW_TOP_CENTER_OTR 9079 +#define IDR_WINDOW_TOP_LEFT_CORNER_OTR 9080 +#define IDR_WINDOW_TOP_RIGHT_CORNER_OTR 9081 +#define IDR_TAB_INACTIVE_CENTER_V 9082 +#define IDR_TAB_INACTIVE_LEFT_V 9083 +#define IDR_TAB_INACTIVE_RIGHT_V 9084 +#define IDR_RESTORE 9085 +#define IDR_RESTORE_H 9086 +#define IDR_RESTORE_P 9087 +#define IDR_DEWINDOW_BOTTOM_CENTER 9088 +#define IDR_DEWINDOW_BOTTOM_LEFT_CORNER 9089 +#define IDR_DEWINDOW_BOTTOM_RIGHT_CORNER 9090 +#define IDR_DEWINDOW_LEFT_SIDE 9091 +#define IDR_DEWINDOW_RIGHT_SIDE 9092 +#define IDR_DEWINDOW_TOP_CENTER 9093 +#define IDR_DEWINDOW_TOP_LEFT_CORNER 9094 +#define IDR_DEWINDOW_TOP_RIGHT_CORNER 9095 +#define IDR_DEWINDOW_BOTTOM_CENTER_OTR 9096 +#define IDR_DEWINDOW_BOTTOM_LEFT_CORNER_OTR 9097 +#define IDR_DEWINDOW_BOTTOM_RIGHT_CORNER_OTR 9098 +#define IDR_DEWINDOW_LEFT_SIDE_OTR 9099 +#define IDR_DEWINDOW_RIGHT_SIDE_OTR 9100 +#define IDR_DEWINDOW_TOP_CENTER_OTR 9101 +#define IDR_DEWINDOW_TOP_LEFT_CORNER_OTR 9102 +#define IDR_DEWINDOW_TOP_RIGHT_CORNER_OTR 9103 +#define IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM 9104 +#define IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_H 9105 +#define IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_P 9106 +#define IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE 9107 +#define IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_H 9108 +#define IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_P 9109 +#define IDR_DOWNLOAD_BUTTON_CENTER_TOP 9110 +#define IDR_DOWNLOAD_BUTTON_CENTER_TOP_H 9111 +#define IDR_DOWNLOAD_BUTTON_CENTER_TOP_P 9112 +#define IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM 9113 +#define IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_H 9114 +#define IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_P 9115 +#define IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE 9116 +#define IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_H 9117 +#define IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_P 9118 +#define IDR_DOWNLOAD_BUTTON_LEFT_TOP 9119 +#define IDR_DOWNLOAD_BUTTON_LEFT_TOP_H 9120 +#define IDR_DOWNLOAD_BUTTON_LEFT_TOP_P 9121 +#define IDR_DOWNLOAD_BUTTON_MENU_BOTTOM 9122 +#define IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_H 9123 +#define IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_P 9124 +#define IDR_DOWNLOAD_BUTTON_MENU_MIDDLE 9125 +#define IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_H 9126 +#define IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_P 9127 +#define IDR_DOWNLOAD_BUTTON_MENU_TOP 9128 +#define IDR_DOWNLOAD_BUTTON_MENU_TOP_H 9129 +#define IDR_DOWNLOAD_BUTTON_MENU_TOP_P 9130 +#define IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM 9131 +#define IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_H 9132 +#define IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_P 9133 +#define IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_NO_DD 9134 +#define IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE 9135 +#define IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_H 9136 +#define IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_P 9137 +#define IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_NO_DD 9138 +#define IDR_DOWNLOAD_BUTTON_RIGHT_TOP 9139 +#define IDR_DOWNLOAD_BUTTON_RIGHT_TOP_H 9140 +#define IDR_DOWNLOAD_BUTTON_RIGHT_TOP_P 9141 +#define IDR_DOWNLOAD_BUTTON_RIGHT_TOP_NO_DD 9142 +#define IDR_DOWNLOAD_PROGRESS_BACKGROUND_16 9143 +#define IDR_DOWNLOAD_PROGRESS_FOREGROUND_16 9144 +#define IDR_DOWNLOAD_PROGRESS_BACKGROUND_32 9145 +#define IDR_DOWNLOAD_PROGRESS_FOREGROUND_32 9146 +#define IDR_DOWNLOAD_ICON 9147 +#define IDR_COOKIE_ICON 9148 +#define IDR_SAD_FAVICON 9149 +#define IDR_TAB_DROP_UP 9150 +#define IDR_TAB_DROP_DOWN 9151 +#define IDR_CONSTRAINED_BOTTOM_CENTER 9152 +#define IDR_CONSTRAINED_BOTTOM_LEFT_CORNER 9153 +#define IDR_CONSTRAINED_BOTTOM_RIGHT_CORNER 9154 +#define IDR_CONSTRAINED_LEFT_SIDE 9155 +#define IDR_CONSTRAINED_RIGHT_SIDE 9156 +#define IDR_CONSTRAINED_TOP_CENTER 9157 +#define IDR_CONSTRAINED_TOP_LEFT_CORNER 9158 +#define IDR_CONSTRAINED_TOP_RIGHT_CORNER 9159 +#define IDR_FIND_BOX_BACKGROUND 9160 +#define IDR_FIND_BOX_BACKGROUND_LEFT 9161 +#define IDR_LOCK 9162 +#define IDR_WARNING 9163 +#define IDR_BOOKMARK_BAR_RECENTLY_BOOKMARKED_ICON 9164 +#define IDR_STOP 9165 +#define IDR_STOP_H 9166 +#define IDR_STOP_P 9167 +#define IDR_FIND_DLG_LEFT_BACKGROUND 9168 +#define IDR_FIND_DLG_RIGHT_BACKGROUND 9169 +#define IDR_FIND_DLG_MIDDLE_BACKGROUND 9170 +#define IDR_APP_TOP_RIGHT 9171 +#define IDR_APP_TOP_CENTER 9172 +#define IDR_APP_TOP_LEFT 9173 +#define IDR_APP_DROPARROW 9174 +#define IDR_THROBBER_01 9175 +#define IDR_THROBBER_02 9176 +#define IDR_THROBBER_03 9177 +#define IDR_THROBBER_04 9178 +#define IDR_THROBBER_05 9179 +#define IDR_THROBBER_06 9180 +#define IDR_THROBBER_07 9181 +#define IDR_THROBBER_08 9182 +#define IDR_THROBBER_09 9183 +#define IDR_THROBBER_10 9184 +#define IDR_THROBBER_11 9185 +#define IDR_THROBBER_12 9186 +#define IDR_THROBBER_13 9187 +#define IDR_THROBBER_14 9188 +#define IDR_THROBBER_15 9189 +#define IDR_THROBBER_16 9190 +#define IDR_THROBBER_17 9191 +#define IDR_THROBBER_18 9192 +#define IDR_THROBBER_19 9193 +#define IDR_THROBBER_20 9194 +#define IDR_THROBBER_21 9195 +#define IDR_THROBBER_22 9196 +#define IDR_THROBBER_23 9197 +#define IDR_THROBBER_24 9198 +#define IDR_PAGEINFO_GOOD 9199 +#define IDR_PAGEINFO_BAD 9200 +#define IDR_NEWTAB_BUTTON 9201 +#define IDR_NEWTAB_BUTTON_H 9202 +#define IDR_NEWTAB_BUTTON_P 9203 +#define IDR_ARROW_RIGHT 9204 +#define IDR_TEXTBUTTON_TOP_LEFT_H 9205 +#define IDR_TEXTBUTTON_TOP_H 9206 +#define IDR_TEXTBUTTON_TOP_RIGHT_H 9207 +#define IDR_TEXTBUTTON_LEFT_H 9208 +#define IDR_TEXTBUTTON_CENTER_H 9209 +#define IDR_TEXTBUTTON_RIGHT_H 9210 +#define IDR_TEXTBUTTON_BOTTOM_LEFT_H 9211 +#define IDR_TEXTBUTTON_BOTTOM_H 9212 +#define IDR_TEXTBUTTON_BOTTOM_RIGHT_H 9213 +#define IDR_TEXTBUTTON_TOP_LEFT_P 9214 +#define IDR_TEXTBUTTON_TOP_P 9215 +#define IDR_TEXTBUTTON_TOP_RIGHT_P 9216 +#define IDR_TEXTBUTTON_LEFT_P 9217 +#define IDR_TEXTBUTTON_CENTER_P 9218 +#define IDR_TEXTBUTTON_RIGHT_P 9219 +#define IDR_TEXTBUTTON_BOTTOM_LEFT_P 9220 +#define IDR_TEXTBUTTON_BOTTOM_P 9221 +#define IDR_TEXTBUTTON_BOTTOM_RIGHT_P 9222 +#define IDR_SAD_TAB 9223 +#define IDR_FOLDER_OPEN 9224 +#define IDR_FOLDER_CLOSED 9225 +#define IDR_OTR_ICON 9226 +#define IDR_TAB_INACTIVE_LEFT_OTR 9227 +#define IDR_TAB_INACTIVE_CENTER_OTR 9228 +#define IDR_TAB_INACTIVE_RIGHT_OTR 9229 +#define IDR_BOOKMARK_BAR_CHEVRONS 9230 +#define IDR_CONSTRAINED_BOTTOM_CENTER_V 9231 +#define IDR_CONSTRAINED_BOTTOM_LEFT_CORNER_V 9232 +#define IDR_CONSTRAINED_BOTTOM_RIGHT_CORNER_V 9233 +#define IDR_CONSTRAINED_LEFT_SIDE_V 9234 +#define IDR_CONSTRAINED_RIGHT_SIDE_V 9235 +#define IDR_CONSTRAINED_TOP_CENTER_V 9236 +#define IDR_CONSTRAINED_TOP_LEFT_CORNER_V 9237 +#define IDR_CONSTRAINED_TOP_RIGHT_CORNER_V 9238 +#define IDR_CONTENT_STAR_D 9239 +#define IDR_CONTENT_STAR_OFF 9240 +#define IDR_CONTENT_STAR_ON 9241 +#define IDR_LOCATION_BAR_KEYWORD_HINT_TAB 9242 +#define IDR_ABOUT_BACKGROUND 9243 +#define IDR_FINDINPAGE_PREV 9244 +#define IDR_FINDINPAGE_PREV_H 9245 +#define IDR_FINDINPAGE_PREV_P 9246 +#define IDR_FINDINPAGE_NEXT 9247 +#define IDR_FINDINPAGE_NEXT_H 9248 +#define IDR_FINDINPAGE_NEXT_P 9249 +#define IDR_INFOBAR_RESTORE_SESSION 9250 +#define IDR_INFOBAR_SAVE_PASSWORD 9251 +#define IDR_INFOBAR_SSL_WARNING 9252 +#define IDR_INFOBAR_ALT_NAV_URL 9253 +#define IDR_INFOBAR_PLUGIN_INSTALL 9254 +#define IDR_INFO_BUBBLE_CORNER_TOP_LEFT 9255 +#define IDR_INFO_BUBBLE_CORNER_TOP_RIGHT 9256 +#define IDR_INFO_BUBBLE_CORNER_BOTTOM_LEFT 9257 +#define IDR_INFO_BUBBLE_CORNER_BOTTOM_RIGHT 9258 +#define IDR_WIZARD_ICON 9259 +#define IDR_MENU_MARKER 9260 +#define IDR_FROZEN_TAB_ICON 9261 +#define IDR_FROZEN_PLUGIN_ICON 9262 +#define IDR_UPDATE_AVAILABLE 9263 +#define IDR_MENU_PAGE 9264 +#define IDR_MENU_CHROME 9265 +#define IDR_ABOUT_BACKGROUND_RTL 9266 +#define IDR_WIZARD_ICON_RTL 9267 +#define IDR_LOCATIONBG_POPUPMODE_LEFT 9268 +#define IDR_LOCATIONBG_POPUPMODE_CENTER 9269 +#define IDR_LOCATIONBG_POPUPMODE_RIGHT 9270 +#define IDR_CLOSE_SA 9271 +#define IDR_CLOSE_SA_H 9272 +#define IDR_CLOSE_SA_P 9273 +#define IDR_HISTORY_SECTION 9274 +#define IDR_DOWNLOADS_SECTION 9275 +#define IDR_DEFAULT_THUMBNAIL 9276 +#define IDR_THROBBER_WAITING 9277 +#define IDR_INFOBAR_PLUGIN_CRASHED 9278 +#define IDR_UPDATE_UPTODATE 9279 +#define IDR_UPDATE_FAIL 9280 +#define IDR_CLOSE_BAR 9281 +#define IDR_CLOSE_BAR_H 9282 +#define IDR_CLOSE_BAR_P 9283 +#define IDR_HOME 9284 +#define IDR_HOME_H 9285 +#define IDR_HOME_P 9286 +#define IDR_FIND_BOX_BACKGROUND_LEFT_RTL 9287 +#define IDR_INPUT_GOOD 9288 +#define IDR_INPUT_ALERT 9289 +#define IDR_HISTORY_FAVICON 9290 +#define IDR_DOWNLOADS_FAVICON 9291 +#define IDR_MENU_PAGE_RTL 9292 +#define IDR_MENU_CHROME_RTL 9293 +#define IDR_DOWNLOAD_ANIMATION_BEGIN 9294 +#define IDR_TAB_HOVER_LEFT 9295 +#define IDR_TAB_HOVER_CENTER 9296 +#define IDR_TAB_HOVER_RIGHT 9297 +#define IDR_FOLDER_CLOSED_RTL 9298 +#define IDR_FOLDER_OPEN_RTL 9299 +#define IDR_BOOKMARK_BAR_FOLDER 9300 +#define IDR_FIND_DLG_LEFT_BB_BACKGROUND 9301 +#define IDR_FIND_DLG_RIGHT_BB_BACKGROUND 9302 +#define IDR_FIND_DLG_MIDDLE_BB_BACKGROUND 9303 +#define IDR_THROBBER_LIGHT 9304 +#define IDR_OTR_ICON_STANDALONE 9305 +#define IDR_PRODUCT_LOGO 9306 +#define IDR_DISTRIBUTOR_LOGO 9307 +#define IDR_DISTRIBUTOR_LOGO_LIGHT 9308 diff --git a/chrome/app/theme/theme_resources.rc b/chrome/app/theme/theme_resources.rc index 8f78a69..58c3e8b 100644 --- a/chrome/app/theme/theme_resources.rc +++ b/chrome/app/theme/theme_resources.rc @@ -145,12 +145,15 @@ IDR_DOWNLOAD_BUTTON_MENU_TOP_P BINDATA "download_button_menu_top_p.png" IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM BINDATA "download_button_right_bottom.png" IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_H BINDATA "download_button_right_bottom_h.png" IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_P BINDATA "download_button_right_bottom_p.png" +IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_NO_DD BINDATA "download_button_right_bottom_no_dd.png" IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE BINDATA "download_button_right_middle.png" IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_H BINDATA "download_button_right_middle_h.png" IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_P BINDATA "download_button_right_middle_p.png" +IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_NO_DD BINDATA "download_button_right_middle_no_dd.png" IDR_DOWNLOAD_BUTTON_RIGHT_TOP BINDATA "download_button_right_top.png" IDR_DOWNLOAD_BUTTON_RIGHT_TOP_H BINDATA "download_button_right_top_h.png" IDR_DOWNLOAD_BUTTON_RIGHT_TOP_P BINDATA "download_button_right_top_p.png" +IDR_DOWNLOAD_BUTTON_RIGHT_TOP_NO_DD BINDATA "download_button_right_top_no_dd.png" IDR_DOWNLOAD_PROGRESS_BACKGROUND_16 BINDATA "download_progress_background16.png" IDR_DOWNLOAD_PROGRESS_FOREGROUND_16 BINDATA "download_progress_foreground16.png" IDR_DOWNLOAD_PROGRESS_BACKGROUND_32 BINDATA "download_progress_background32.png" diff --git a/chrome/browser/download/download_exe.cc b/chrome/browser/download/download_exe.cc index c9fdc4d..ce33781 100644 --- a/chrome/browser/download/download_exe.cc +++ b/chrome/browser/download/download_exe.cc @@ -59,6 +59,7 @@ static const wchar_t* const g_executables[] = { L"app", L"application", L"asp", + L"asx", L"bas", L"bat", L"chm", @@ -66,6 +67,7 @@ static const wchar_t* const g_executables[] = { L"com", L"cpl", L"crt", + L"dll", L"exe", L"fxp", L"hlp", @@ -73,6 +75,7 @@ static const wchar_t* const g_executables[] = { L"inf", L"ins", L"isp", + L"jar", L"js", L"jse", L"lnk", diff --git a/chrome/browser/download/download_file.cc b/chrome/browser/download/download_file.cc index 4fa3637..6b0fba4 100644 --- a/chrome/browser/download/download_file.cc +++ b/chrome/browser/download/download_file.cc @@ -578,3 +578,9 @@ void DownloadFileManager::CreateDirectory(const std::wstring& directory) { if (!file_util::PathExists(directory)) file_util::CreateDirectory(directory); } + +void DownloadFileManager::DeleteFile(const std::wstring& path) { + // Make sure we only delete files. + if (file_util::PathExists(path) && !file_util::DirectoryExists(path)) + file_util::Delete(path, false); +} diff --git a/chrome/browser/download/download_file.h b/chrome/browser/download/download_file.h index 5dc5d63..cce6398 100644 --- a/chrome/browser/download/download_file.h +++ b/chrome/browser/download/download_file.h @@ -210,6 +210,9 @@ class DownloadFileManager // download directory exists. void CreateDirectory(const std::wstring& directory); + // Called by the donwload manager to delete non validated dangerous downloads. + void DeleteFile(const std::wstring& path); + private: // Timer helpers for updating the UI about the current progress of a download. void StartUpdateTimer(); diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc index d83ddc2..cff6935 100644 --- a/chrome/browser/download/download_manager.cc +++ b/chrome/browser/download/download_manager.cc @@ -15,6 +15,7 @@ #include "base/task.h" #include "base/thread.h" #include "base/timer.h" +#include "base/rand_util.h" #include "base/win_util.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" @@ -97,6 +98,7 @@ static bool DownloadPathIsDangerous(const std::wstring& download_path) { DownloadItem::DownloadItem(const DownloadCreateInfo& info) : id_(-1), full_path_(info.path), + original_name_(info.original_name), url_(info.url), total_bytes_(info.total_bytes), received_bytes_(info.received_bytes), @@ -105,6 +107,7 @@ DownloadItem::DownloadItem(const DownloadCreateInfo& info) start_time_(info.start_time), db_handle_(info.db_handle), manager_(NULL), + safety_state_(SAFE), is_paused_(false), open_when_complete_(false), render_process_id_(-1), @@ -118,13 +121,16 @@ DownloadItem::DownloadItem(const DownloadCreateInfo& info) DownloadItem::DownloadItem(int32 download_id, const std::wstring& path, const std::wstring& url, + const std::wstring& original_name, const Time start_time, int64 download_size, int render_process_id, - int request_id) + int request_id, + bool is_dangerous) : id_(download_id), full_path_(path), url_(url), + original_name_(original_name), total_bytes_(download_size), received_bytes_(0), start_tick_(GetTickCount()), @@ -132,6 +138,7 @@ DownloadItem::DownloadItem(int32 download_id, start_time_(start_time), db_handle_(kUninitializedHandle), manager_(NULL), + safety_state_(is_dangerous ? DANGEROUS : SAFE), is_paused_(false), open_when_complete_(false), render_process_id_(render_process_id), @@ -198,14 +205,16 @@ void DownloadItem::Cancel(bool update_history) { void DownloadItem::Finished(int64 size) { state_ = COMPLETE; UpdateSize(size); - UpdateObservers(); StopProgressTimer(); } -void DownloadItem::Remove() { +void DownloadItem::Remove(bool delete_on_disk) { Cancel(true); state_ = REMOVING; + if (delete_on_disk) + manager_->DeleteDownload(full_path_); manager_->RemoveDownload(db_handle_); + // We are now deleted. } void DownloadItem::StartProgressTimer() { @@ -255,6 +264,12 @@ void DownloadItem::TogglePause() { UpdateObservers(); } +std::wstring DownloadItem::GetFileName() const { + if (safety_state_ == DownloadItem::SAFE) + return file_name_; + return original_name_; +} + // DownloadManager implementation ---------------------------------------------- // static @@ -313,12 +328,19 @@ void DownloadManager::Shutdown() { // 'in_progress_' may contain DownloadItems that have not finished the start // complete (from the history service) and thus aren't in downloads_. DownloadMap::iterator it = in_progress_.begin(); + std::set<DownloadItem*> to_remove; for (; it != in_progress_.end(); ++it) { DownloadItem* download = it->second; - if (download->state() == DownloadItem::IN_PROGRESS) { - download->Cancel(false); - UpdateHistoryForDownload(download); + if (download->safety_state() == DownloadItem::DANGEROUS) { + // Forget about any download that the user did not approve. + // Note that we cannot call download->Remove() this would invalidate our + // iterator. + to_remove.insert(download); + continue; } + DCHECK_EQ(DownloadItem::IN_PROGRESS, download->state()); + download->Cancel(false); + UpdateHistoryForDownload(download); if (download->db_handle() == kUninitializedHandle) { // An invalid handle means that 'download' does not yet exist in // 'downloads_', so we have to delete it here. @@ -326,7 +348,25 @@ void DownloadManager::Shutdown() { } } + // 'dangerous_finished_' contains all complete downloads that have not been + // approved. They should be removed. + it = dangerous_finished_.begin(); + for (; it != dangerous_finished_.end(); ++it) + to_remove.insert(it->second); + + // Remove the dangerous download that are not approved. + for (std::set<DownloadItem*>::const_iterator rm_it = to_remove.begin(); + rm_it != to_remove.end(); ++rm_it) { + DownloadItem* download = *rm_it; + download->Remove(true); + // Same as above, delete the download if it is not in 'downloads_'. + if (download->db_handle() == kUninitializedHandle) + delete download; + } + to_remove.clear(); + in_progress_.clear(); + dangerous_finished_.clear(); STLDeleteValues(&downloads_); file_manager_ = NULL; @@ -486,17 +526,36 @@ void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info) { // Check writability of the suggested path. If we can't write to it, default // to the user's "My Documents" directory. We'll prompt them in this case. - std::wstring path = file_util::GetDirectoryFromPath(info->suggested_path); - if (!file_util::PathIsWritable(path)) { + std::wstring dir = file_util::GetDirectoryFromPath(info->suggested_path); + const std::wstring filename = + file_util::GetFilenameFromPath(info->suggested_path); + if (!file_util::PathIsWritable(dir)) { info->save_as = true; - const std::wstring filename = - file_util::GetFilenameFromPath(info->suggested_path); PathService::Get(chrome::DIR_USER_DOCUMENTS, &info->suggested_path); file_util::AppendToPath(&info->suggested_path, filename); } info->suggested_path_exists = !UniquifyPath(&info->suggested_path); + // If the download is deemmed dangerous, we'll use a temporary name for it. + if (!info->save_as && IsDangerous(filename)) { + info->original_name = file_util::GetFilenameFromPath(info->suggested_path); + // Create a temporary file to hold the file until the user approves its + // download. + std::wstring file_name; + std::wstring path; + while (path.empty()) { + SStringPrintf(&file_name, L"dangerous_download_%d.download", + base::RandInt(0, 100000)); + path = dir; + file_util::AppendToPath(&path, file_name); + if (file_util::PathExists(path)) + path.clear(); + } + info->suggested_path = path; + info->is_dangerous = true; + } + // Now we return to the UI thread. ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, @@ -537,10 +596,12 @@ void DownloadManager::ContinueStartDownload(DownloadCreateInfo* info, download = new DownloadItem(info->download_id, info->path, info->url, + info->original_name, info->start_time, info->total_bytes, info->render_process_id, - info->request_id); + info->request_id, + info->is_dangerous); download->set_manager(this); in_progress_[info->download_id] = download; } else { @@ -628,30 +689,7 @@ void DownloadManager::UpdateDownload(int32 download_id, int64 size) { void DownloadManager::DownloadFinished(int32 download_id, int64 size) { DownloadMap::iterator it = in_progress_.find(download_id); - if (it != in_progress_.end()) { - // Remove the id from the list of pending ids. - PendingFinishedMap::iterator erase_it = - pending_finished_downloads_.find(download_id); - if (erase_it != pending_finished_downloads_.end()) - pending_finished_downloads_.erase(erase_it); - - DownloadItem* download = it->second; - download->Finished(size); - - // Open the download if the user or user prefs indicate it should be. - const std::wstring extension = - file_util::GetFileExtensionFromPath(download->full_path()); - if (download->open_when_complete() || ShouldOpenFileExtension(extension)) - OpenDownloadInShell(download, NULL); - - // Clean up will happen when the history system create callback runs if we - // don't have a valid db_handle yet. - if (download->db_handle() != kUninitializedHandle) { - in_progress_.erase(it); - NotifyAboutDownloadStop(); - UpdateHistoryForDownload(download); - } - } else { + if (it == in_progress_.end()) { // The download is done, but the user hasn't selected a final location for // it yet (the Save As dialog box is probably still showing), so just keep // track of the fact that this download id is complete, when the @@ -660,7 +698,102 @@ void DownloadManager::DownloadFinished(int32 download_id, int64 size) { pending_finished_downloads_.find(download_id); DCHECK(erase_it == pending_finished_downloads_.end()); pending_finished_downloads_[download_id] = size; + return; + } + + // Remove the id from the list of pending ids. + PendingFinishedMap::iterator erase_it = + pending_finished_downloads_.find(download_id); + if (erase_it != pending_finished_downloads_.end()) + pending_finished_downloads_.erase(erase_it); + + DownloadItem* download = it->second; + download->Finished(size); + + // Clean up will happen when the history system create callback runs if we + // don't have a valid db_handle yet. + if (download->db_handle() != kUninitializedHandle) { + in_progress_.erase(it); + NotifyAboutDownloadStop(); + UpdateHistoryForDownload(download); + } + + // If this a dangerous download not yet validated by the user, don't do + // anything. When the user notifies us, it will trigger a call to + // ProceedWithFinishedDangerousDownload. + if (download->safety_state() == DownloadItem::DANGEROUS) { + dangerous_finished_[download_id] = download; + return; + } + + if (download->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED) { + // We first need to rename the donwloaded file from its temporary name to + // its final name before we can continue. + file_loop_->PostTask(FROM_HERE, + NewRunnableMethod( + this, &DownloadManager::ProceedWithFinishedDangerousDownload, + download->db_handle(), + download->full_path(), download->original_name())); + return; } + ContinueDownloadFinished(download); +} + +void DownloadManager::ContinueDownloadFinished(DownloadItem* download) { + // If this was a dangerous download, it has now been approved and must be + // removed from dangerous_finished_ so it does not get deleted on shutdown. + DownloadMap::iterator it = dangerous_finished_.find(download->id()); + if (it != dangerous_finished_.end()) + dangerous_finished_.erase(it); + + // Notify our observers that we are complete (the call to Finished() set the + // state to complete but did not notify). + download->UpdateObservers(); + + // Open the download if the user or user prefs indicate it should be. + const std::wstring extension = + file_util::GetFileExtensionFromPath(download->full_path()); + if (download->open_when_complete() || ShouldOpenFileExtension(extension)) + OpenDownloadInShell(download, NULL); +} + +// Called on the file thread. Renames the downloaded file to its original name. +void DownloadManager::ProceedWithFinishedDangerousDownload( + int64 download_handle, + const std::wstring& path, + const std::wstring& original_name) { + bool success = false; + std::wstring new_path = path; + if (file_util::PathExists(path)) { + new_path = file_util::GetDirectoryFromPath(new_path); + file_util::AppendToPath(&new_path, original_name); + success = file_util::Move(path, new_path); + } else { + NOTREACHED(); + } + + ui_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &DownloadManager::DangerousDownloadRenamed, + download_handle, success, new_path)); +} + +// Call from the file thread when the finished dangerous download was renamed. +void DownloadManager::DangerousDownloadRenamed(int64 download_handle, + bool success, + const std::wstring& new_path) { + DownloadMap::iterator it = downloads_.find(download_handle); + if (it == downloads_.end()) { + NOTREACHED(); + return; + } + + DownloadItem* download = it->second; + // If we failed to rename the file, we'll just keep the name as is. + if (success) + RenameDownload(download, new_path); + + // Continue the download finished sequence. + ContinueDownloadFinished(download); } // static @@ -741,6 +874,27 @@ void DownloadManager::OnPauseDownloadRequest(ResourceDispatcherHost* rdh, rdh->PauseRequest(render_process_id, request_id, pause); } +bool DownloadManager::IsDangerous(const std::wstring& file_name) { + // TODO(jcampan): Improve me. + return IsExecutable(file_util::GetFileExtensionFromPath(file_name)); +} + +void DownloadManager::RenameDownload(DownloadItem* download, + const std::wstring& new_path) { + download->Rename(new_path); + + // Update the history. + + // No update necessary if the download was initiated while in incognito mode. + if (download->db_handle() <= kUninitializedHandle) + return; + + // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong. + HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); + if (hs) + hs->UpdateDownloadPath(new_path, download->db_handle()); +} + void DownloadManager::RemoveDownload(int64 download_handle) { DownloadMap::iterator it = downloads_.find(download_handle); if (it == downloads_.end()) @@ -752,6 +906,9 @@ void DownloadManager::RemoveDownload(int64 download_handle) { // Remove from our tables and delete. downloads_.erase(it); + it = dangerous_finished_.find(download->id()); + if (it != dangerous_finished_.end()) + dangerous_finished_.erase(it); delete download; // Tell observers to refresh their views. @@ -1014,6 +1171,29 @@ void DownloadManager::FileSelectionCanceled(void* params) { info->download_id)); } +void DownloadManager::DeleteDownload(const std::wstring& path) { + file_loop_->PostTask(FROM_HERE, NewRunnableMethod( + file_manager_, &DownloadFileManager::DeleteFile, path)); +} + + +void DownloadManager::DangerousDownloadValidated(DownloadItem* download) { + DCHECK_EQ(DownloadItem::DANGEROUS, download->safety_state()); + download->set_safety_state(DownloadItem::DANGEROUS_BUT_VALIDATED); + download->UpdateObservers(); + + // If the download is not complete, nothing to do. The required + // post-processing will be performed when it does complete. + if (download->state() != DownloadItem::COMPLETE) + return; + + file_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, + &DownloadManager::ProceedWithFinishedDangerousDownload, + download->db_handle(), download->full_path(), + download->original_name())); +} + // Operations posted to us from the history service ---------------------------- // The history service has retrieved all download entries. 'entries' contains diff --git a/chrome/browser/download/download_manager.h b/chrome/browser/download/download_manager.h index 0ab3dc1..6e78fb6 100644 --- a/chrome/browser/download/download_manager.h +++ b/chrome/browser/download/download_manager.h @@ -80,6 +80,12 @@ class DownloadItem { REMOVING }; + enum SafetyState { + SAFE = 0, + DANGEROUS, + DANGEROUS_BUT_VALIDATED // Dangerous but the user confirmed the download. + }; + // Interface that observers of a particular download must implement in order // to receive updates to the download's status. class Observer { @@ -94,10 +100,12 @@ class DownloadItem { DownloadItem(int32 download_id, const std::wstring& path, const std::wstring& url, + const std::wstring& original_name, const Time start_time, int64 download_size, int render_process_id, - int request_id); + int request_id, + bool is_dangerous); ~DownloadItem(); @@ -125,12 +133,12 @@ class DownloadItem { // when resuming a download (assuming the server supports byte ranges). void Cancel(bool update_history); - // Download operation completed + // Download operation completed. void Finished(int64 size); - // The user wants to remove the download from the views and history. This - // operation does not delete the file on the disk. - void Remove(); + // The user wants to remove the download from the views and history. If + // |delete_file| is true, the file is deleted on the disk. + void Remove(bool delete_file); // Start/stop sending periodic updates to our observers void StartProgressTimer(); @@ -158,8 +166,10 @@ class DownloadItem { // Accessors DownloadState state() const { return state_; } - std::wstring full_path() const { return full_path_; } std::wstring file_name() const { return file_name_; } + void set_file_name(const std::wstring& name) { file_name_ = name; } + std::wstring full_path() const { return full_path_; } + void set_full_path(const std::wstring& path) { full_path_ = path; } std::wstring url() const { return url_; } int64 total_bytes() const { return total_bytes_; } void set_total_bytes(int64 total_bytes) { total_bytes_ = total_bytes; } @@ -176,6 +186,16 @@ class DownloadItem { void set_open_when_complete(bool open) { open_when_complete_ = open; } int render_process_id() const { return render_process_id_; } int request_id() const { return request_id_; } + SafetyState safety_state() const { return safety_state_; } + void set_safety_state(SafetyState safety_state) { + safety_state_ = safety_state; + } + std::wstring original_name() const { return original_name_; } + void set_original_name(const std::wstring& name) { original_name_ = name; } + + // Returns the file-name that should be reported to the user, which is + // file_name_ for safe downloads and original_name_ for dangerous ones. + std::wstring GetFileName() const; private: // Internal helper for maintaining consistent received and total sizes. @@ -226,6 +246,14 @@ class DownloadItem { // A flag for indicating if the download should be opened at completion. bool open_when_complete_; + // Whether the download is considered potentially safe or dangerous + // (executable files are typically considered dangerous). + SafetyState safety_state_; + + // Dangerous download are given temporary names until the user approves them. + // This stores their original name. + std::wstring original_name_; + // For canceling or pausing requests. int render_process_id_; int request_id_; @@ -354,6 +382,12 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, virtual void FileSelected(const std::wstring& path, void* params); virtual void FileSelectionCanceled(void* params); + // Deletes the specified path on the file thread. + void DeleteDownload(const std::wstring& path); + + // Called when the user has validated the donwload of a dangerous file. + void DangerousDownloadValidated(DownloadItem* download); + private: // Shutdown the download manager. This call is needed only after Init. void Shutdown(); @@ -405,6 +439,32 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, int request_id, bool pause); + // Performs the last steps required when a download has been completed. + // It is necessary to break down the flow when a download is finished as + // dangerous downloads are downloaded to temporary files that need to be + // renamed on the file thread first. + // Invoked on the UI thread. + void ContinueDownloadFinished(DownloadItem* download); + + // Renames a finished dangerous download from its temporary file name to its + // real file name. + // Invoked on the file thread. + void ProceedWithFinishedDangerousDownload(int64 download_handle, + const std::wstring& path, + const std::wstring& original_name); + + // Invoked on the UI thread when a dangerous downloaded file has been renamed. + void DangerousDownloadRenamed(int64 download_handle, + bool success, + const std::wstring& new_path); + + // Checks whether a file represents a risk if downloaded. + bool IsDangerous(const std::wstring& file_name); + + // Changes the paths and file name of the specified |download|, propagating + // the change to the history system. + void RenameDownload(DownloadItem* download, const std::wstring& new_path); + // 'downloads_' is map of all downloads in this profile. The key is the handle // returned by the history system, which is unique across sessions. This map // owns all the DownloadItems once they have been created in the history @@ -415,6 +475,12 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, // ResourceDispatcherHost, which is unique for the current session. This map // does not own the DownloadItems. // + // 'dangerous_finished_' is a map of dangerous download that have finished + // but were not yet approved by the user. Similarly to in_progress_, the key + // is the ID assigned by the ResourceDispatcherHost and the map does not own + // the DownloadItems. It is used on shutdown to delete completed downloads + // that have not been approved. + // // When a download is created through a user action, the corresponding // DownloadItem* is placed in 'in_progress_' and remains there until it has // received a valid handle from the history system. Once it has a valid @@ -426,6 +492,7 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, typedef base::hash_map<int64, DownloadItem*> DownloadMap; DownloadMap downloads_; DownloadMap in_progress_; + DownloadMap dangerous_finished_; // True if the download manager has been initialized and requires a shutdown. bool shutdown_needed_; diff --git a/chrome/browser/download/download_util.cc b/chrome/browser/download/download_util.cc index 530eade..96de46c 100644 --- a/chrome/browser/download/download_util.cc +++ b/chrome/browser/download/download_util.cc @@ -133,7 +133,7 @@ void BaseContextMenu::ExecuteCommand(int id) { break; } case REMOVE_ITEM: - download_->Remove(); + download_->Remove(false); break; case CANCEL: download_->Cancel(true); diff --git a/chrome/browser/download/save_package.cc b/chrome/browser/download/save_package.cc index 127fa53..ff56be2 100644 --- a/chrome/browser/download/save_package.cc +++ b/chrome/browser/download/save_package.cc @@ -126,7 +126,7 @@ SavePackage::~SavePackage() { // We call this to remove the view from the shelf. It will invoke // DownloadManager::RemoveDownload, but since the fake DownloadItem is not // owned by DownloadManager, it will do nothing to our fake item. - download_->Remove(); + download_->Remove(false); delete download_; download_ = NULL; } @@ -174,8 +174,8 @@ bool SavePackage::Init() { } // Create the fake DownloadItem and display the view. - download_ = new DownloadItem(1, saved_main_file_path_, - page_url_, Time::Now(), 0, -1, -1); + download_ = new DownloadItem(1, saved_main_file_path_, page_url_, + std::wstring(), Time::Now(), 0, -1, -1, false); download_->set_manager(web_contents_->profile()->GetDownloadManager()); DownloadShelfView* shelf = web_contents_->GetDownloadShelfView(); shelf->AddDownloadView(new DownloadItemView( diff --git a/chrome/browser/history/download_database.cc b/chrome/browser/history/download_database.cc index 1baf976..e604e5d 100644 --- a/chrome/browser/history/download_database.cc +++ b/chrome/browser/history/download_database.cc @@ -93,6 +93,20 @@ bool DownloadDatabase::UpdateDownload(int64 received_bytes, return statement->step() == SQLITE_DONE; } +bool DownloadDatabase::UpdateDownloadPath(const std::wstring& path, + DownloadID db_handle) { + DCHECK(db_handle > 0); + SQLITE_UNIQUE_STATEMENT(statement, GetStatementCache(), + "UPDATE downloads " + "SET full_path=? WHERE id=?"); + if (!statement.is_valid()) + return false; + + statement->bind_wstring(0, path); + statement->bind_int64(1, db_handle); + return statement->step() == SQLITE_DONE; +} + int64 DownloadDatabase::CreateDownload(const DownloadCreateInfo& info) { SQLITE_UNIQUE_STATEMENT(statement, GetStatementCache(), "INSERT INTO downloads " diff --git a/chrome/browser/history/download_database.h b/chrome/browser/history/download_database.h index 1fc9812..d733546 100644 --- a/chrome/browser/history/download_database.h +++ b/chrome/browser/history/download_database.h @@ -27,6 +27,9 @@ class DownloadDatabase { // Update the state of one download. Returns true if successful. bool UpdateDownload(int64 received_bytes, int32 state, DownloadID db_handle); + // Update the path of one download. Returns true if successful. + bool UpdateDownloadPath(const std::wstring& path, DownloadID db_handle); + // Create a new database entry for one download and return its primary db id. int64 CreateDownload(const DownloadCreateInfo& info); diff --git a/chrome/browser/history/download_types.h b/chrome/browser/history/download_types.h index 16f3ad7..5b6e5b4 100644 --- a/chrome/browser/history/download_types.h +++ b/chrome/browser/history/download_types.h @@ -37,7 +37,8 @@ struct DownloadCreateInfo { render_view_id(-1), request_id(-1), db_handle(0), - save_as(false) { + save_as(false), + is_dangerous(false) { } DownloadCreateInfo() : download_id(-1) {} @@ -59,6 +60,10 @@ struct DownloadCreateInfo { std::string content_disposition; std::string mime_type; bool save_as; + // Whether this download is potentially dangerous (ex: exe, dll, ...). + bool is_dangerous; + // The original name for a dangerous download. + std::wstring original_name; }; #endif // CHROME_BROWSER_DOWNLOAD_TYPES_H__ diff --git a/chrome/browser/history/history.cc b/chrome/browser/history/history.cc index db04446..92bea22 100644 --- a/chrome/browser/history/history.cc +++ b/chrome/browser/history/history.cc @@ -469,6 +469,12 @@ void HistoryService::UpdateDownload(int64 received_bytes, received_bytes, state, db_handle); } +void HistoryService::UpdateDownloadPath(const std::wstring& path, + int64 db_handle) { + ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownloadPath, + path, db_handle); +} + void HistoryService::RemoveDownload(int64 db_handle) { ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::RemoveDownload, db_handle); diff --git a/chrome/browser/history/history.h b/chrome/browser/history/history.h index 74ef67b..2aee029 100644 --- a/chrome/browser/history/history.h +++ b/chrome/browser/history/history.h @@ -421,6 +421,10 @@ class HistoryService : public CancelableRequestProvider, // the database with no need for a callback. void UpdateDownload(int64 received_bytes, int32 state, int64 db_handle); + // Called to update the history service about the path of a download. + // This is a 'fire and forget' query. + void UpdateDownloadPath(const std::wstring& path, int64 db_handle); + // Permanently remove a download from the history system. This is a 'fire and // forget' operation. void RemoveDownload(int64 db_handle); diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc index 21f77fb..f8e6449 100644 --- a/chrome/browser/history/history_backend.cc +++ b/chrome/browser/history/history_backend.cc @@ -934,6 +934,13 @@ void HistoryBackend::UpdateDownload(int64 received_bytes, db_->UpdateDownload(received_bytes, state, db_handle); } +// Update the path of a particular download entry. +void HistoryBackend::UpdateDownloadPath(const std::wstring& path, + int64 db_handle) { + if (db_.get()) + db_->UpdateDownloadPath(path, db_handle); +} + // Create a new download entry and pass back the db_handle to it. void HistoryBackend::CreateDownload( scoped_refptr<DownloadCreateRequest> request, diff --git a/chrome/browser/history/history_backend.h b/chrome/browser/history/history_backend.h index dd37c3e..e646570 100644 --- a/chrome/browser/history/history_backend.h +++ b/chrome/browser/history/history_backend.h @@ -193,6 +193,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, void QueryDownloads(scoped_refptr<DownloadQueryRequest> request); void UpdateDownload(int64 received_bytes, int32 state, int64 db_handle); + void UpdateDownloadPath(const std::wstring& path, int64 db_handle); void CreateDownload(scoped_refptr<DownloadCreateRequest> request, const DownloadCreateInfo& info); void RemoveDownload(int64 db_handle); diff --git a/chrome/browser/resource_dispatcher_host.cc b/chrome/browser/resource_dispatcher_host.cc index e6035f8..4b0f37c 100644 --- a/chrome/browser/resource_dispatcher_host.cc +++ b/chrome/browser/resource_dispatcher_host.cc @@ -302,6 +302,7 @@ class ResourceDispatcherHost::DownloadEventHandler info->content_disposition = content_disposition_; info->mime_type = response->response_head.mime_type; info->save_as = save_as_; + info->is_dangerous = false; download_manager_->file_loop()->PostTask(FROM_HERE, NewRunnableMethod(download_manager_, &DownloadFileManager::StartDownload, diff --git a/chrome/browser/views/download_item_view.cc b/chrome/browser/views/download_item_view.cc index 2dcba5a..6d0af1e3 100644 --- a/chrome/browser/views/download_item_view.cc +++ b/chrome/browser/views/download_item_view.cc @@ -6,6 +6,8 @@ #include <vector> +#include "base/file_util.h" +#include "base/string_util.h" #include "chrome/app/theme/theme_resources.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_util.h" @@ -14,6 +16,7 @@ #include "chrome/common/l10n_util.h" #include "chrome/common/resource_bundle.h" #include "chrome/common/win_util.h" +#include "chrome/views/native_button.h" #include "chrome/views/root_view.h" #include "chrome/views/view_container.h" @@ -23,15 +26,27 @@ // animation is added, and also possibly to take into account // different screen resolutions. static const int kTextWidth = 140; // Pixels +static const int kDangerousTextWidth = 200; // Pixels static const int kHorizontalTextPadding = 2; // Pixels static const int kVerticalPadding = 3; // Pixels static const int kVerticalTextSpacer = 2; // Pixels static const int kVerticalTextPadding = 2; // Pixels +// The maximum number of characters we show in a file name when displaying the +// dangerous download message. +static const int kFileNameMaxLength = 20; + // We add some padding before the left image so that the progress animation icon // hides the corners of the left image. static const int kLeftPadding = 0; // Pixels. +// The space between the Save and Discard buttons when prompting for a dangerous +// donwload. +static const int kButtonPadding = 5; // Pixels. + +// The space on the left and right side of the dangerous donwload label. +static const int kLabelPadding = 4; // Pixels. + static const SkColor kFileNameColor = SkColorSetRGB(87, 108, 149); static const SkColor kStatusColor = SkColorSetRGB(123, 141, 174); @@ -50,11 +65,16 @@ DownloadItemView::DownloadItemView(DownloadItem* download, body_state_(NORMAL), drop_down_state_(NORMAL), drop_down_pressed_(false), - file_name_(download_->file_name()), status_text_(l10n_util::GetString(IDS_DOWNLOAD_STATUS_STARTING)), show_status_text_(true), dragging_(false), - starting_drag_(false) { + starting_drag_(false), + warning_icon_(NULL), + save_button_(NULL), + discard_button_(NULL), + dangerous_download_label_(NULL), + dangerous_download_label_sized_(false), + cached_button_size_(0, 0) { // TODO(idana) Bug# 1163334 // // We currently do not mirror each download item on the download shelf (even @@ -131,6 +151,19 @@ DownloadItemView::DownloadItemView(DownloadItem* download, }; pushed_drop_down_image_set_ = pushed_drop_down_image_set; + BodyImageSet dangerous_mode_body_image_set = { + rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_TOP), + rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE), + rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM), + rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_TOP), + rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE), + rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM), + rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_TOP_NO_DD), + rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_NO_DD), + rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_NO_DD) + }; + dangerous_mode_body_image_set_ = dangerous_mode_body_image_set; + LoadIcon(); font_ = ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont); @@ -152,6 +185,33 @@ DownloadItemView::DownloadItemView(DownloadItem* download, body_hover_animation_.reset(new SlideAnimation(this)); drop_hover_animation_.reset(new SlideAnimation(this)); + if (download->safety_state() == DownloadItem::DANGEROUS) { + body_state_ = DANGEROUS; + drop_down_state_ = DANGEROUS; + + warning_icon_ = rb.GetBitmapNamed(IDR_WARNING); + save_button_ = new ChromeViews::NativeButton( + l10n_util::GetString(IDS_SAVE_DOWNLOAD)); + save_button_->set_enforce_dlu_min_size(false); + save_button_->SetListener(this); + discard_button_ = new ChromeViews::NativeButton( + l10n_util::GetString(IDS_DISCARD_DOWNLOAD)); + discard_button_->SetListener(this); + discard_button_->set_enforce_dlu_min_size(false); + AddChildView(save_button_); + AddChildView(discard_button_); + std::wstring file_name = download->original_name(); + // Ensure the file name is not too long. + ElideString(file_name, kFileNameMaxLength, &file_name); + dangerous_download_label_ = new ChromeViews::Label( + l10n_util::GetStringF(IDS_PROMPT_DANGEROUS_DOWNLOAD, file_name)); + dangerous_download_label_->SetMultiLine(true); + dangerous_download_label_->SetHorizontalAlignment( + ChromeViews::Label::ALIGN_LEFT); + dangerous_download_label_->SetColor(kFileNameColor); + AddChildView(dangerous_download_label_); + } + // Set up our animation StartDownloadProgress(); } @@ -190,6 +250,12 @@ void DownloadItemView::StopDownloadProgress() { void DownloadItemView::OnDownloadUpdated(DownloadItem* download) { DCHECK(download == download_); + if (body_state_ == DANGEROUS && + download->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED) { + // We have been approved. + ClearDangerousMode(); + } + std::wstring status_text = model_->GetStatusText(); switch (download_->state()) { case DownloadItem::IN_PROGRESS: @@ -227,18 +293,46 @@ void DownloadItemView::OnDownloadUpdated(DownloadItem* download) { // View overrides +// In dangerous mode we have to layout our buttons. +void DownloadItemView::Layout() { + if (IsDangerousMode()) { + SizeLabelToMinWidth(); + int x = kLeftPadding + dangerous_mode_body_image_set_.top_left->width() + + warning_icon_->width() + kLabelPadding; + int y = (height() - dangerous_download_label_->height()) / 2; + dangerous_download_label_->SetBounds(x, y, + dangerous_download_label_->width(), + dangerous_download_label_->height()); + CSize button_size; + GetButtonSize(&button_size); + x += dangerous_download_label_->width() + kLabelPadding; + y = (height() - button_size.cy) / 2; + save_button_->SetBounds(x, y, button_size.cx, button_size.cy); + x += button_size.cx + kButtonPadding; + discard_button_->SetBounds(x, y, button_size.cx, button_size.cy); + } +} + +void DownloadItemView::DidChangeBounds(const CRect& previous, + const CRect& current) { + Layout(); +} + +void DownloadItemView::ButtonPressed(ChromeViews::NativeButton* sender) { + if (sender == discard_button_) { + if (download_->state() == DownloadItem::IN_PROGRESS) + download_->Cancel(true); + download_->Remove(true); + // WARNING: we are deleted at this point. Don't access 'this'. + } else if (sender == save_button_) { + // This will change the state and notify us. + download_->manager()->DangerousDownloadValidated(download_); + } +} + // Load an icon for the file type we're downloading, and animate any in progress // download state. void DownloadItemView::Paint(ChromeCanvas* canvas) { - int center_width = width() - kLeftPadding - - normal_body_image_set_.left->width() - - normal_body_image_set_.right->width() - - normal_drop_down_image_set_.center->width(); - - // May be caused by animation. - if (center_width <= 0) - return; - BodyImageSet* body_image_set; switch (body_state_) { case NORMAL: @@ -248,6 +342,9 @@ void DownloadItemView::Paint(ChromeCanvas* canvas) { case PUSHED: body_image_set = &pushed_body_image_set_; break; + case DANGEROUS: + body_image_set = &dangerous_mode_body_image_set_; + break; default: NOTREACHED(); } @@ -260,10 +357,24 @@ void DownloadItemView::Paint(ChromeCanvas* canvas) { case PUSHED: drop_down_image_set = &pushed_drop_down_image_set_; break; + case DANGEROUS: + drop_down_image_set = NULL; // No drop-down in dangerous mode. + break; default: NOTREACHED(); } + int center_width = width() - kLeftPadding - + body_image_set->left->width() - + body_image_set->right->width() - + (drop_down_image_set ? + normal_drop_down_image_set_.center->width() : + 0); + + // May be caused by animation. + if (center_width <= 0) + return; + // Paint the background images. int x = kLeftPadding; PaintBitmaps(canvas, @@ -302,65 +413,76 @@ void DownloadItemView::Paint(ChromeCanvas* canvas) { PaintBitmaps(canvas, hot_body_image_set_.top_right, hot_body_image_set_.right, hot_body_image_set_.bottom_right, - x, box_y_, box_height_, hot_body_image_set_.top_right->width()); + x, box_y_, box_height_, + hot_body_image_set_.top_right->width()); canvas->restore(); } x += body_image_set->top_right->width(); - PaintBitmaps(canvas, - drop_down_image_set->top, drop_down_image_set->center, - drop_down_image_set->bottom, - x, box_y_, box_height_, drop_down_image_set->top->width()); - - // Overlay our drop-down hot state. - if (drop_hover_animation_->GetCurrentValue() > 0) { - canvas->saveLayerAlpha(NULL, - static_cast<int>(drop_hover_animation_->GetCurrentValue() * 255), - SkCanvas::kARGB_NoClipLayer_SaveFlag); - canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode); + // Paint the drop-down. + if (drop_down_image_set) { PaintBitmaps(canvas, drop_down_image_set->top, drop_down_image_set->center, drop_down_image_set->bottom, x, box_y_, box_height_, drop_down_image_set->top->width()); - canvas->restore(); + // Overlay our drop-down hot state. + if (drop_hover_animation_->GetCurrentValue() > 0) { + canvas->saveLayerAlpha(NULL, + static_cast<int>(drop_hover_animation_->GetCurrentValue() * 255), + SkCanvas::kARGB_NoClipLayer_SaveFlag); + canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode); + + PaintBitmaps(canvas, + drop_down_image_set->top, drop_down_image_set->center, + drop_down_image_set->bottom, + x, box_y_, box_height_, drop_down_image_set->top->width()); + + canvas->restore(); + } } // Print the text, left aligned. // Last value of x was the end of the right image, just before the button. - if (show_status_text_) { - int y = box_y_ + kVerticalPadding; - canvas->DrawStringInt(file_name_, font_, kFileNameColor, - download_util::kSmallProgressIconSize, y, - kTextWidth, font_.height()); - y += font_.height() + kVerticalTextPadding; - - canvas->DrawStringInt(status_text_, font_, kStatusColor, - download_util::kSmallProgressIconSize, y, - kTextWidth, font_.height()); - } else { - int y = box_y_ + (box_height_ - font_.height()) / 2; - canvas->DrawStringInt(file_name_, font_, kFileNameColor, - download_util::kSmallProgressIconSize, y, - kTextWidth, font_.height()); + // Note that in dangerous mode we use a label (as the text is multi-line). + if (!IsDangerousMode()) { + if (show_status_text_) { + int y = box_y_ + kVerticalPadding; + canvas->DrawStringInt(download_->GetFileName(), font_, kFileNameColor, + download_util::kSmallProgressIconSize, y, + kTextWidth, font_.height()); + y += font_.height() + kVerticalTextPadding; + + canvas->DrawStringInt(status_text_, font_, kStatusColor, + download_util::kSmallProgressIconSize, y, + kTextWidth, font_.height()); + } else { + int y = box_y_ + (box_height_ - font_.height()) / 2; + canvas->DrawStringInt(download_->GetFileName(), font_, kFileNameColor, + download_util::kSmallProgressIconSize, y, + kTextWidth, font_.height()); + } } // Paint the icon. IconManager* im = g_browser_process->icon_manager(); - SkBitmap* icon = im->LookupIcon(download_->full_path(), IconLoader::SMALL); + SkBitmap* icon = IsDangerousMode() ? warning_icon_ : + im->LookupIcon(download_->full_path(), IconLoader::SMALL); if (icon) { - if (download_->state() == DownloadItem::IN_PROGRESS) { - download_util::PaintDownloadProgress(canvas, this, 0, 0, - progress_angle_, - download_->PercentComplete(), - download_util::SMALL); - } else if (download_->state() == DownloadItem::COMPLETE && - complete_animation_->IsAnimating()) { - download_util::PaintDownloadComplete(canvas, this, 0, 0, - complete_animation_->GetCurrentValue(), - download_util::SMALL); + if (!IsDangerousMode()) { + if (download_->state() == DownloadItem::IN_PROGRESS) { + download_util::PaintDownloadProgress(canvas, this, 0, 0, + progress_angle_, + download_->PercentComplete(), + download_util::SMALL); + } else if (download_->state() == DownloadItem::COMPLETE && + complete_animation_->IsAnimating()) { + download_util::PaintDownloadComplete(canvas, this, 0, 0, + complete_animation_->GetCurrentValue(), + download_util::SMALL); + } } // Draw the icon image @@ -401,20 +523,65 @@ void DownloadItemView::SetState(State body_state, State drop_down_state) { SchedulePaint(); } -void DownloadItemView::GetPreferredSize(CSize* out) { - int width = kLeftPadding + normal_body_image_set_.top_left->width(); - width += download_util::kSmallProgressIconSize; - width += kTextWidth; - width += normal_body_image_set_.top_right->width(); - width += normal_drop_down_image_set_.top->width(); +void DownloadItemView::ClearDangerousMode() { + DCHECK(download_->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED && + body_state_ == DANGEROUS && drop_down_state_ == DANGEROUS); + + body_state_ = NORMAL; + drop_down_state_ = NORMAL; + + // Remove the views used by the dangerours mode. + RemoveChildView(save_button_); + delete save_button_; + save_button_ = NULL; + RemoveChildView(discard_button_); + delete discard_button_; + discard_button_ = NULL; + RemoveChildView(dangerous_download_label_); + delete dangerous_download_label_; + dangerous_download_label_ = NULL; + + // We need to load the icon now that the download_ has the real path. + LoadIcon(); + + // Force the shelf to layout again as our size has changed. + parent_->Layout(); + parent_->SchedulePaint(); +} +void DownloadItemView::GetPreferredSize(CSize* out) { + int width, height; + if (IsDangerousMode()) { + width = kLeftPadding + dangerous_mode_body_image_set_.top_left->width(); + width += warning_icon_->width() + kLabelPadding; + width += dangerous_download_label_->width() + kLabelPadding; + CSize button_size; + GetButtonSize(&button_size); + width += button_size.cx * 2 + kButtonPadding; + width += dangerous_mode_body_image_set_.top_right->width(); + height = std::max<int>(2 * kVerticalPadding + 2 * font_.height() + + kVerticalTextPadding, + 2 * kVerticalPadding + warning_icon_->height()); + height = std::max<int>(height, 2 * kVerticalPadding + button_size.cy); + } else { + width = kLeftPadding + normal_body_image_set_.top_left->width(); + width += download_util::kSmallProgressIconSize; + width += kTextWidth; + width += normal_body_image_set_.top_right->width(); + width += normal_drop_down_image_set_.top->width(); + height = std::max<int>(2 * kVerticalPadding + 2 * font_.height() + + kVerticalTextPadding, + download_util::kSmallProgressIconSize); + } out->cx = width; - out->cy = std::max<int>( - 2 * kVerticalPadding + 2 * font_.height() + kVerticalTextPadding, - download_util::kSmallProgressIconSize); + out->cy = height; } void DownloadItemView::OnMouseExited(const ChromeViews::MouseEvent& event) { + // Mouse should not activate us in dangerous mode. + if (IsDangerousMode()) + return; + SetState(NORMAL, drop_down_pressed_ ? PUSHED : NORMAL); body_hover_animation_->Hide(); drop_hover_animation_->Hide(); @@ -422,6 +589,10 @@ void DownloadItemView::OnMouseExited(const ChromeViews::MouseEvent& event) { // Display the context menu for this item. bool DownloadItemView::OnMousePressed(const ChromeViews::MouseEvent& event) { + // Mouse should not activate us in dangerous mode. + if (IsDangerousMode()) + return true; + // Stop any completion animation. if (complete_animation_.get() && complete_animation_->IsAnimating()) complete_animation_->End(); @@ -472,6 +643,10 @@ bool DownloadItemView::OnMousePressed(const ChromeViews::MouseEvent& event) { } void DownloadItemView::OnMouseMoved(const ChromeViews::MouseEvent& event) { + // Mouse should not activate us in dangerous mode. + if (IsDangerousMode()) + return; + bool on_body = event.x() < drop_down_x_; SetState(on_body ? HOT : NORMAL, on_body ? NORMAL : HOT); if (on_body) { @@ -485,6 +660,10 @@ void DownloadItemView::OnMouseMoved(const ChromeViews::MouseEvent& event) { void DownloadItemView::OnMouseReleased(const ChromeViews::MouseEvent& event, bool canceled) { + // Mouse should not activate us in dangerous mode. + if (IsDangerousMode()) + return; + if (dragging_) { // Starting a drag results in a MouseReleased, we need to ignore it. dragging_ = false; @@ -499,6 +678,10 @@ void DownloadItemView::OnMouseReleased(const ChromeViews::MouseEvent& event, // Handle drag (file copy) operations. bool DownloadItemView::OnMouseDragged(const ChromeViews::MouseEvent& event) { + // Mouse should not activate us in dangerous mode. + if (IsDangerousMode()) + return true; + if (!starting_drag_) { starting_drag_ = true; drag_start_point_ = event.location(); @@ -544,3 +727,71 @@ void DownloadItemView::LoadIcon() { NewCallback(this, &DownloadItemView::OnExtractIconComplete)); } +void DownloadItemView::GetButtonSize(CSize* size) { + DCHECK(save_button_ && discard_button_); + // We cache the size when successfully retrieved, not for performance reasons + // but because if this DownloadItemView is being animated while the tab is + // not showing, the native buttons are not parented and their preferred size + // is 0, messing-up the layout. + if (cached_button_size_.cx != 0) { + *size = cached_button_size_; + } + + CSize tmp_size; + save_button_->GetMinimumSize(size); + discard_button_->GetMinimumSize(&tmp_size); + + size->cx = std::max(size->cx, tmp_size.cx); + size->cy = std::max(size->cy, tmp_size.cy); + + if (size->cx != 0) { + cached_button_size_.cx = size->cx; + cached_button_size_.cy = size->cy; + } +} + +// This method computes the miminum width of the label for diplaying its text +// on 2 lines. It just breaks the string in 2 lines on the spaces and keeps the +// configuration with minimum width. +void DownloadItemView::SizeLabelToMinWidth() { + if (dangerous_download_label_sized_) + return; + + std::wstring text = dangerous_download_label_->GetText(); + TrimWhitespace(text, TRIM_ALL, &text); + DCHECK_EQ(std::wstring::npos, text.find(L"\n")); + + // Make the label big so that GetPreferredSize() is not constrained by the + // current width. + dangerous_download_label_->SetBounds(0, 0, 1000, 1000); + + CSize size(0, 0); + int min_width = -1; + int sp_index = text.find(L" "); + while (sp_index != std::wstring::npos) { + text.replace(sp_index, 1, L"\n"); + dangerous_download_label_->SetText(text); + dangerous_download_label_->GetPreferredSize(&size); + + if (min_width == -1) + min_width = size.cx; + + // If thw width is growing again, it means we passed the optimal width spot. + if (size.cx > min_width) + break; + else + min_width = size.cx; + + // Restore the string. + text.replace(sp_index, 1, L" "); + + sp_index = text.find(L" ", sp_index + 1); + } + + // If we have a line with no space, we won't cut it. + if (min_width == -1) + dangerous_download_label_->GetPreferredSize(&size); + + dangerous_download_label_->SetBounds(0, 0, size.cx, size.cy); + dangerous_download_label_sized_ = true; +}
\ No newline at end of file diff --git a/chrome/browser/views/download_item_view.h b/chrome/browser/views/download_item_view.h index 69adcc4..c29a154 100644 --- a/chrome/browser/views/download_item_view.h +++ b/chrome/browser/views/download_item_view.h @@ -26,13 +26,17 @@ #include "chrome/browser/download/download_manager.h" #include "chrome/browser/icon_manager.h" #include "chrome/views/event.h" +#include "chrome/views/native_button.h" #include "chrome/views/view.h" -#include "chrome/views/label.h" +namespace ChromeViews { + class Label; +} class DownloadShelfView; class SkBitmap; -class DownloadItemView : public ChromeViews::View, +class DownloadItemView : public ChromeViews::NativeButton::Listener, + public ChromeViews::View, public DownloadItem::Observer, public AnimationDelegate { public: @@ -56,6 +60,7 @@ class DownloadItemView : public ChromeViews::View, virtual void OnDownloadUpdated(DownloadItem* download); // View overrides + virtual void Layout(); virtual void Paint(ChromeCanvas* canvas); virtual void GetPreferredSize(CSize *out); virtual void OnMouseExited(const ChromeViews::MouseEvent& event); @@ -64,6 +69,10 @@ class DownloadItemView : public ChromeViews::View, virtual void OnMouseReleased(const ChromeViews::MouseEvent& event, bool canceled); virtual bool OnMouseDragged(const ChromeViews::MouseEvent& event); + virtual void DidChangeBounds(const CRect& previous, const CRect& current); + + // NativeButton::Listener implementation. + virtual void ButtonPressed(ChromeViews::NativeButton* sender); // AnimationDelegate implementation. virtual void AnimationProgressed(const Animation* animation); @@ -81,6 +90,7 @@ class DownloadItemView : public ChromeViews::View, NORMAL = 0, HOT, PUSHED, + DANGEROUS }; // The image set associated with the part containing the icon and text. @@ -121,14 +131,33 @@ class DownloadItemView : public ChromeViews::View, // Sets the state and triggers a repaint. void SetState(State body_state, State drop_down_state); + // Whether we are in the dangerous mode. + bool IsDangerousMode() { return body_state_ == DANGEROUS; } + + // Reverts from dangerous mode to normal download mode. + void ClearDangerousMode(); + + // Sets |size| with the size of the Save and Discard buttons (they have the + // same size). + void GetButtonSize(CSize* size); + + // Sizes the dangerous download label to a minimum width available using 2 + // lines. The size is computed only the first time this method is invoked + // and simply returned on subsequent calls. + void SizeLabelToMinWidth(); + // The different images used for the background. BodyImageSet normal_body_image_set_; BodyImageSet hot_body_image_set_; BodyImageSet pushed_body_image_set_; + BodyImageSet dangerous_mode_body_image_set_; DropDownImageSet normal_drop_down_image_set_; DropDownImageSet hot_drop_down_image_set_; DropDownImageSet pushed_drop_down_image_set_; + // The warning icon showns for dangerous downloads. + SkBitmap* warning_icon_; + // The model we query for display information DownloadItem* download_; @@ -136,7 +165,6 @@ class DownloadItemView : public ChromeViews::View, DownloadShelfView* parent_; // Elements of our particular download - std::wstring file_name_; std::wstring status_text_; bool show_status_text_; @@ -189,6 +217,19 @@ class DownloadItemView : public ChromeViews::View, // Progress animation base::RepeatingTimer<DownloadItemView> progress_timer_; + // Dangerous mode buttons. + ChromeViews::NativeButton* save_button_; + ChromeViews::NativeButton* discard_button_; + + // Dangerous mode label. + ChromeViews::Label* dangerous_download_label_; + + // Whether the dangerous mode label has been sized yet. + bool dangerous_download_label_sized_; + + // The size of the buttons. Cached so animation works when hidden. + CSize cached_button_size_; + DISALLOW_EVIL_CONSTRUCTORS(DownloadItemView); }; diff --git a/chrome/browser/views/download_tab_view.cc b/chrome/browser/views/download_tab_view.cc index 62e5668..679aa54 100644 --- a/chrome/browser/views/download_tab_view.cc +++ b/chrome/browser/views/download_tab_view.cc @@ -30,7 +30,8 @@ // Approximate spacing, in pixels, taken from initial UI mock up screens static const int kVerticalPadding = 5; -static const int kHorizontalButtonPadding = 15; +static const int kHorizontalLinkPadding = 15; +static const int kHorizontalButtonPadding = 8; // For vertical and horizontal element spacing static const int kSpacer = 20; @@ -65,12 +66,19 @@ static const SkColor kUrlColor = SkColorSetRGB(0, 128, 0); // Paused download indicator (red) static const SkColor kPauseColor = SkColorSetRGB(128, 0, 0); +// Warning label color (blue) +static const SkColor kWarningColor = SkColorSetRGB(87, 108, 149); + // Selected item background color static const SkColor kSelectedItemColor = SkColorSetRGB(215, 232, 255); // State key used to identify search text. static const wchar_t kSearchTextKey[] = L"st"; +// The maximum number of characters we show in a file name when displaying the +// dangerous download message. +static const int kFileNameMaxLength = 20; + // Sorting functor for DownloadItem -------------------------------------------- // Sort DownloadItems into ascending order by their start time. @@ -87,7 +95,8 @@ class DownloadItemSorter : public std::binary_function<DownloadItem*, // DownloadItemTabView implementation ------------------------------------------ DownloadItemTabView::DownloadItemTabView() : model_(NULL), - parent_(NULL) { + parent_(NULL), + is_floating_view_renderer_(false) { // Create our element views using empty strings for now, // set them based on the model's state in Layout(). since_ = new ChromeViews::Label(L""); @@ -111,6 +120,29 @@ DownloadItemTabView::DownloadItemTabView() file_name_->SetFont(font); AddChildView(file_name_); + // dangerous_download_warning_ is enabled when a dangerous download has been + // initiated. + dangerous_download_warning_ = new ChromeViews::Label(); + dangerous_download_warning_ ->SetMultiLine(true); + dangerous_download_warning_->SetColor(kWarningColor); + dangerous_download_warning_->SetHorizontalAlignment( + ChromeViews::Label::ALIGN_LEFT); + dangerous_download_warning_->SetFont(font); + AddChildView(dangerous_download_warning_); + + // The save and discard buttons are shown to prompt the user when a dangerous + // download was started. + save_button_ = new ChromeViews::NativeButton( + l10n_util::GetString(IDS_SAVE_DOWNLOAD)); + save_button_->set_enforce_dlu_min_size(false); + save_button_->SetListener(this); + discard_button_ = new ChromeViews::NativeButton( + l10n_util::GetString(IDS_DISCARD_DOWNLOAD)); + discard_button_->SetListener(this); + discard_button_->set_enforce_dlu_min_size(false); + AddChildView(save_button_); + AddChildView(discard_button_); + // Set our URL name download_url_ = new ChromeViews::Label(L""); download_url_->SetColor(kUrlColor); @@ -172,9 +204,9 @@ void DownloadItemTabView::GetPreferredSize(CSize* out) { out->cx = download_util::kBigProgressIconSize + 2 * kSpacer + - kHorizontalButtonPadding + + kHorizontalLinkPadding + kFilenameSize + - std::max(pause_size.cx + cancel_size.cx + kHorizontalButtonPadding, + std::max(pause_size.cx + cancel_size.cx + kHorizontalLinkPadding, show_size.cx); out->cy = download_util::kBigProgressIconSize; @@ -188,13 +220,19 @@ void DownloadItemTabView::Layout() { DCHECK(model_); switch (model_->state()) { case DownloadItem::COMPLETE: - LayoutComplete(); + if (model_->safety_state() == DownloadItem::DANGEROUS) + LayoutPromptDangerousDownload(); + else + LayoutComplete(); break; case DownloadItem::CANCELLED: LayoutCancelled(); break; case DownloadItem::IN_PROGRESS: - LayoutInProgress(); + if (model_->safety_state() == DownloadItem::DANGEROUS) + LayoutPromptDangerousDownload(); + else + LayoutInProgress(); break; case DownloadItem::REMOVING: break; @@ -238,6 +276,11 @@ void DownloadItemTabView::LayoutComplete() { cancel_->SetEnabled(false); time_remaining_->SetVisible(false); download_progress_->SetVisible(false); + dangerous_download_warning_->SetVisible(false); + save_button_->SetVisible(false); + save_button_->SetEnabled(false); + discard_button_->SetVisible(false); + discard_button_->SetEnabled(false); LayoutDate(); int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset + @@ -286,6 +329,11 @@ void DownloadItemTabView::LayoutCancelled() { pause_->SetEnabled(false); cancel_->SetVisible(false); cancel_->SetEnabled(false); + dangerous_download_warning_->SetVisible(false); + save_button_->SetVisible(false); + save_button_->SetEnabled(false); + discard_button_->SetVisible(false); + discard_button_->SetEnabled(false); LayoutDate(); int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset + @@ -372,6 +420,11 @@ void DownloadItemTabView::LayoutInProgress() { // Hide unused UI elements show_->SetVisible(false); show_->SetEnabled(false); + dangerous_download_warning_->SetVisible(false); + save_button_->SetVisible(false); + save_button_->SetEnabled(false); + discard_button_->SetVisible(false); + discard_button_->SetEnabled(false); LayoutDate(); int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset + @@ -380,7 +433,7 @@ void DownloadItemTabView::LayoutInProgress() { // File name and URL, truncated to show progress status CSize file_name_size; - file_name_->SetText(model_->file_name()); + file_name_->SetText(model_->GetFileName()); file_name_->GetPreferredSize(&file_name_size); file_name_->SetBounds(dx, download_util::kBigProgressIconOffset, kFilenameSize - kProgressSize - kSpacer, @@ -514,7 +567,7 @@ void DownloadItemTabView::LayoutInProgress() { pause_->GetPreferredSize(&pause_size); pause_->SetBounds(dx, y_pos, pause_size.cx, pause_size.cy); - dx += pause_size.cx + kHorizontalButtonPadding; + dx += pause_size.cx + kHorizontalLinkPadding; CSize cancel_size; cancel_->GetPreferredSize(&cancel_size); @@ -523,10 +576,69 @@ void DownloadItemTabView::LayoutInProgress() { cancel_->SetEnabled(true); } +void DownloadItemTabView::LayoutPromptDangerousDownload() { + // Hide unused UI elements + show_->SetVisible(false); + show_->SetEnabled(false); + file_name_->SetVisible(false); + file_name_->SetEnabled(false); + pause_->SetVisible(false); + pause_->SetEnabled(false); + cancel_->SetVisible(false); + cancel_->SetEnabled(false); + time_remaining_->SetVisible(false); + download_progress_->SetVisible(false); + + LayoutDate(); + int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset + + download_util::kBigProgressIconSize + + kInfoPadding; + + // Warning message and URL. + CSize warning_size; + std::wstring file_name; + ElideString(model_->original_name(), kFileNameMaxLength, &file_name); + dangerous_download_warning_->SetText( + l10n_util::GetStringF(IDS_PROMPT_DANGEROUS_DOWNLOAD, file_name)); + dangerous_download_warning_->GetPreferredSize(&warning_size); + dangerous_download_warning_->SetBounds(dx, 0, + kFilenameSize, warning_size.cy); + dangerous_download_warning_->SetVisible(true); + + GURL url(model_->url()); + download_url_->SetURL(url); + CSize url_size; + download_url_->GetPreferredSize(&url_size); + download_url_->SetBounds(dx, height() - url_size.cy, + std::min(kFilenameSize - kSpacer, + static_cast<int>(width() - dx)), + url_size.cy); + download_url_->SetVisible(true); + + dx += kFilenameSize + kSpacer; + + // Save/Discard buttons. + CSize button_size; + save_button_->GetPreferredSize(&button_size); + save_button_->SetBounds(dx, (height() - button_size.cy) / 2, + button_size.cx, button_size.cy); + save_button_->SetVisible(true); + save_button_->SetEnabled(true); + + dx += button_size.cx + kHorizontalButtonPadding; + + discard_button_->GetPreferredSize(&button_size); + discard_button_->SetBounds(dx, (height() - button_size.cy) / 2, + button_size.cx, button_size.cy); + discard_button_->SetVisible(true); + discard_button_->SetEnabled(true); +} + void DownloadItemTabView::Paint(ChromeCanvas* canvas) { PaintBackground(canvas); - if (model_->state() == DownloadItem::IN_PROGRESS) { + if (model_->state() == DownloadItem::IN_PROGRESS && + model_->safety_state() != DownloadItem::DANGEROUS) { download_util::PaintDownloadProgress(canvas, this, kDownloadIconOffset - @@ -605,7 +717,10 @@ bool DownloadItemTabView::OnMousePressed(const ChromeViews::MouseEvent& event) { if (select_rect.PtInRect(point)) { parent_->ItemBecameSelected(model_); - if (event.IsRightMouseButton()) { + // Don't show the right-click menu if we are prompting the user for a + // dangerous download. + if (event.IsRightMouseButton() && + model_->safety_state() != DownloadItem::DANGEROUS) { ChromeViews::View::ConvertPointToScreen(this, &point); download_util::DownloadDestinationContextMenu menu( @@ -620,7 +735,8 @@ bool DownloadItemTabView::OnMousePressed(const ChromeViews::MouseEvent& event) { // Handle drag (file copy) operations. bool DownloadItemTabView::OnMouseDragged(const ChromeViews::MouseEvent& event) { - if (model_->state() != DownloadItem::COMPLETE) + if (model_->state() != DownloadItem::COMPLETE || + model_->safety_state() == DownloadItem::DANGEROUS) return false; CPoint point(event.x(), event.y()); @@ -665,6 +781,18 @@ void DownloadItemTabView::LinkActivated(ChromeViews::Link* source, parent_->ItemBecameSelected(model_); } +void DownloadItemTabView::ButtonPressed(ChromeViews::NativeButton* sender) { + if (sender == save_button_) { + parent_->model()->DangerousDownloadValidated(model_); + // Relayout and repaint to display the right mode (complete or in progress). + Layout(); + SchedulePaint(); + } else if (sender == discard_button_) { + model_->Remove(true); + } else { + NOTREACHED(); + } +} // DownloadTabView implementation ---------------------------------------------- @@ -683,6 +811,7 @@ DownloadTabView::~DownloadTabView() { // DownloadManager owns the contents. downloads_.clear(); ClearDownloadInProgress(); + ClearDangerousDownloads(); icon_consumer_.CancelAllRequests(); } @@ -720,6 +849,21 @@ void DownloadTabView::DidChangeBounds(const CRect& previous, void DownloadTabView::Layout() { CRect r; DetachAllFloatingViews(); + // Dangerous downloads items use NativeButtons, so they need to be attached + // as NativeControls are not supported yet in floating views. + gfx::Rect visible_bounds = GetVisibleBounds(); + int row_start = (visible_bounds.y() - kSpacer) / + (download_util::kBigProgressIconSize + kSpacer); + int row_stop = (visible_bounds.y() - kSpacer + visible_bounds.height()) / + (download_util::kBigProgressIconSize + kSpacer); + row_stop = std::min(row_stop, static_cast<int>(downloads_.size()) - 1); + for (int i = row_start; i <= row_stop; ++i) { + // The DownloadManager stores downloads earliest first, but this view + // displays latest first, so adjust the index: + int index = static_cast<int>(downloads_.size()) - 1 - i; + if (downloads_[index]->safety_state() == DownloadItem::DANGEROUS) + ValidateFloatingViewForID(index); + } View* v = GetParent(); if (v) { v->GetLocalBounds(&r, true); @@ -790,12 +934,15 @@ ChromeViews::View* DownloadTabView::CreateFloatingViewForIndex(int index) { } DownloadItemTabView* dl = new DownloadItemTabView(); + // We attach the view before layout as the Save/Discard buttons are native + // and need to be in the tree hierarchy to compute their preferred size + // correctly. + AttachFloatingView(dl, index); dl->SetModel(downloads_[index], this); int row = static_cast<int>(downloads_.size()) - 1 - index; int y_pos = row * (download_util::kBigProgressIconSize + kSpacer) + kSpacer; dl->SetBounds(0, y_pos, width(), download_util::kBigProgressIconSize); dl->Layout(); - AttachFloatingView(dl, index); return dl; } @@ -817,7 +964,10 @@ void DownloadTabView::OnDownloadUpdated(DownloadItem* download) { case DownloadItem::CANCELLED: { base::hash_set<DownloadItem*>::iterator d = in_progress_.find(download); if (d != in_progress_.end()) { - (*d)->RemoveObserver(this); + // If this is a dangerous download not yet validated by the user, we + // still need to be notified when the validation happens. + if (download->safety_state() != DownloadItem::DANGEROUS) + (*d)->RemoveObserver(this); in_progress_.erase(d); } if (in_progress_.empty()) @@ -877,6 +1027,7 @@ void DownloadTabView::OnDownloadUpdated(DownloadItem* download) { void DownloadTabView::ModelChanged() { downloads_.clear(); ClearDownloadInProgress(); + ClearDangerousDownloads(); DetachAllFloatingViews(); // Issue the query. @@ -890,6 +1041,7 @@ void DownloadTabView::SetDownloads(std::vector<DownloadItem*>& downloads) { // Clear out old state and remove self as observer for each download. downloads_.clear(); ClearDownloadInProgress(); + ClearDangerousDownloads(); // Swap new downloads in. downloads_.swap(downloads); @@ -902,6 +1054,10 @@ void DownloadTabView::SetDownloads(std::vector<DownloadItem*>& downloads) { if (download->state() == DownloadItem::IN_PROGRESS) { download->AddObserver(this); in_progress_.insert(download); + } else if (download->safety_state() == DownloadItem::DANGEROUS) { + // We need to be notified when the user validates the dangerous download. + download->AddObserver(this); + dangerous_downloads_.insert(download); } } @@ -949,6 +1105,14 @@ void DownloadTabView::ClearDownloadInProgress() { in_progress_.clear(); } +void DownloadTabView::ClearDangerousDownloads() { + base::hash_set<DownloadItem*>::const_iterator it; + for (it = dangerous_downloads_.begin(); + it != dangerous_downloads_.end(); ++it) + (*it)->RemoveObserver(this); + dangerous_downloads_.clear(); +} + // Check to see if the download is the latest download on a given day. // We use this to determine when to draw the date next to a particular // download view: if the DownloadItem is the latest download on a given diff --git a/chrome/browser/views/download_tab_view.h b/chrome/browser/views/download_tab_view.h index 5c1b2ec..f78ee4a 100644 --- a/chrome/browser/views/download_tab_view.h +++ b/chrome/browser/views/download_tab_view.h @@ -25,7 +25,8 @@ class Timer; } class DownloadItemTabView : public ChromeViews::View, - public ChromeViews::LinkController { + public ChromeViews::LinkController, + public ChromeViews::NativeButton::Listener { public: DownloadItemTabView(); virtual ~DownloadItemTabView(); @@ -44,10 +45,14 @@ class DownloadItemTabView : public ChromeViews::View, void LayoutComplete(); void LayoutCancelled(); void LayoutInProgress(); + void LayoutPromptDangerousDownload(); // LinkController overrides virtual void LinkActivated(ChromeViews::Link* source, int event_flags); + // NativeButton Listener overrides. + virtual void ButtonPressed(ChromeViews::NativeButton* sender); + // Used to set our model temporarily during layout and paint operations void SetModel(DownloadItem* model, DownloadTabView* parent); @@ -58,6 +63,9 @@ private: // Containing view. DownloadTabView* parent_; + // Whether we are the renderer for floating views. + bool is_floating_view_renderer_; + // Time display. ChromeViews::Label* since_; ChromeViews::Label* date_; @@ -72,11 +80,19 @@ private: ChromeViews::Label* time_remaining_; ChromeViews::Label* download_progress_; + // The message warning of a dangerous download. + ChromeViews::Label* dangerous_download_warning_; + // Actions that can be initiated. ChromeViews::Link* pause_; ChromeViews::Link* cancel_; ChromeViews::Link* show_; + // The buttons used to prompt the user when a dangerous download has been + // initiated. + ChromeViews::NativeButton* save_button_; + ChromeViews::NativeButton* discard_button_; + DISALLOW_EVIL_CONSTRUCTORS(DownloadItemTabView); }; @@ -154,10 +170,14 @@ class DownloadTabView : public ChromeViews::View, // Initiates an asynchronous icon extraction. void LoadIcon(DownloadItem* download); - // Clears the list of "in progress" downloads and removes the this - // DownloadTabView from their observer list. + // Clears the list of "in progress" downloads and removes this DownloadTabView + // from their observer list. void ClearDownloadInProgress(); + // Clears the list of dangerous downloads and removes this DownloadTabView + // from their observer list. + void ClearDangerousDownloads(); + // Our model DownloadManager* model_; @@ -178,6 +198,10 @@ class DownloadTabView : public ChromeViews::View, // does not own the DownloadItems. base::hash_set<DownloadItem*> in_progress_; + // Keeps track of the downloads we are an observer for as a consequence of + // being a dangerous download. + base::hash_set<DownloadItem*> dangerous_downloads_; + // Provide a start position for downloads with no known size. int start_angle_; diff --git a/chrome/views/native_button.cc b/chrome/views/native_button.cc index 2db380b..55323c6 100644 --- a/chrome/views/native_button.cc +++ b/chrome/views/native_button.cc @@ -5,17 +5,20 @@ #include "chrome/views/native_button.h" #include "base/logging.h" +#include "chrome/common/gfx/chrome_canvas.h" #include "chrome/common/l10n_util.h" #include "chrome/common/resource_bundle.h" #include "chrome/views/background.h" namespace ChromeViews { -NativeButton::NativeButton(const std::wstring& label) { +NativeButton::NativeButton(const std::wstring& label) + : enforce_dlu_min_size_(true) { Init(label, false); } -NativeButton::NativeButton(const std::wstring& label, bool is_default) { +NativeButton::NativeButton(const std::wstring& label, bool is_default) + : enforce_dlu_min_size_(true) { Init(label, is_default); } @@ -41,13 +44,16 @@ void NativeButton::GetPreferredSize(CSize *out) { sz.cx += 2 * padding_.cx; sz.cy += 2 * padding_.cy; - if (min_dlu_size_.width()) - sz.cx = std::max(static_cast<int>(sz.cx), - font_.horizontal_dlus_to_pixels(min_dlu_size_.width())); - if (min_dlu_size_.height()) - sz.cy = std::max(static_cast<int>(sz.cy), - font_.vertical_dlus_to_pixels(min_dlu_size_.height())); - + if (enforce_dlu_min_size_) { + if (min_dlu_size_.width()) { + sz.cx = + std::max(static_cast<int>(sz.cx), + font_.horizontal_dlus_to_pixels(min_dlu_size_.width())); + } + if (min_dlu_size_.height()) + sz.cy = std::max(static_cast<int>(sz.cy), + font_.vertical_dlus_to_pixels(min_dlu_size_.height())); + } *out = sz; } } @@ -186,4 +192,3 @@ bool NativeButton::OnKeyDown(int virtual_key_code) { } } - diff --git a/chrome/views/native_button.h b/chrome/views/native_button.h index 9a42439..935349f 100644 --- a/chrome/views/native_button.h +++ b/chrome/views/native_button.h @@ -80,6 +80,10 @@ class NativeButton : public NativeControl { // Assigns an accessible string name. void SetAccessibleName(const std::wstring& name); + // Sets whether the min size of this button should follow the Windows + // guidelines. Default is true. Set this to false if you want slim buttons. + void set_enforce_dlu_min_size(bool value) { enforce_dlu_min_size_ = value; } + protected: virtual HWND CreateNativeControl(HWND parent_container); @@ -106,6 +110,10 @@ class NativeButton : public NativeControl { void Clicked(); + // Whether the button preferred size should follow the Microsoft layout + // guidelines. Default is true. + bool enforce_dlu_min_size_; + std::wstring label_; ChromeFont font_; Listener* listener_; |