diff options
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_; |