1 /* 2 * Copyright (C) 2007 Andrea Anzani <andrea.anzani@gmail.com> 3 * Copyright (C) 2007, 2010 Ryan Leavengood <leavengood@gmail.com> 4 * Copyright (C) 2009 Maxime Simon <simon.maxime@gmail.com> 5 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de> 6 * Copyright (C) 2010 Michael Lotz <mmlr@mlotz.ch> 7 * Copyright (C) 2010 Rene Gollent <rene@gollent.com> 8 * Copyright 2013-2015 Haiku, Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "BrowserWindow.h" 33 34 #include <Alert.h> 35 #include <Application.h> 36 #include <Bitmap.h> 37 #include <Button.h> 38 #include <Catalog.h> 39 #include <CheckBox.h> 40 #include <Clipboard.h> 41 #include <ControlLook.h> 42 #include <Debug.h> 43 #include <Directory.h> 44 #include <Entry.h> 45 #include <File.h> 46 #include <FilePanel.h> 47 #include <FindDirectory.h> 48 #include <GridLayoutBuilder.h> 49 #include <GroupLayout.h> 50 #include <GroupLayoutBuilder.h> 51 #include <IconMenuItem.h> 52 #include <Keymap.h> 53 #include <LayoutBuilder.h> 54 #include <Locale.h> 55 #include <ObjectList.h> 56 #include <MenuBar.h> 57 #include <MenuItem.h> 58 #include <MessageRunner.h> 59 #include <NodeInfo.h> 60 #include <NodeMonitor.h> 61 #include <Path.h> 62 #include <Roster.h> 63 #include <Screen.h> 64 #include <SeparatorView.h> 65 #include <Size.h> 66 #include <SpaceLayoutItem.h> 67 #include <StatusBar.h> 68 #include <StringView.h> 69 #include <TextControl.h> 70 #include <UnicodeChar.h> 71 #include <Url.h> 72 73 #include <map> 74 #include <stdio.h> 75 76 #include "AuthenticationPanel.h" 77 #include "BaseURL.h" 78 #include "BitmapButton.h" 79 #include "BookmarkBar.h" 80 #include "BrowserApp.h" 81 #include "BrowsingHistory.h" 82 #include "CredentialsStorage.h" 83 #include "IconButton.h" 84 #include "NavMenu.h" 85 #include "SettingsKeys.h" 86 #include "SettingsMessage.h" 87 #include "TabManager.h" 88 #include "URLInputGroup.h" 89 #include "WebPage.h" 90 #include "WebView.h" 91 #include "WebViewConstants.h" 92 #include "WindowIcon.h" 93 94 95 #undef B_TRANSLATION_CONTEXT 96 #define B_TRANSLATION_CONTEXT "WebPositive Window" 97 98 99 enum { 100 OPEN_LOCATION = 'open', 101 SAVE_PAGE = 'save', 102 GO_BACK = 'goba', 103 GO_FORWARD = 'gofo', 104 STOP = 'stop', 105 HOME = 'home', 106 GOTO_URL = 'goul', 107 RELOAD = 'reld', 108 SHOW_HIDE_BOOKMARK_BAR = 'shbb', 109 CLEAR_HISTORY = 'clhs', 110 111 CREATE_BOOKMARK = 'crbm', 112 SHOW_BOOKMARKS = 'shbm', 113 114 ZOOM_FACTOR_INCREASE = 'zfin', 115 ZOOM_FACTOR_DECREASE = 'zfdc', 116 ZOOM_FACTOR_RESET = 'zfrs', 117 ZOOM_TEXT_ONLY = 'zfto', 118 119 TOGGLE_FULLSCREEN = 'tgfs', 120 TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN = 'tgah', 121 CHECK_AUTO_HIDE_INTERFACE = 'cahi', 122 123 SHOW_PAGE_SOURCE = 'spgs', 124 125 EDIT_SHOW_FIND_GROUP = 'sfnd', 126 EDIT_HIDE_FIND_GROUP = 'hfnd', 127 EDIT_FIND_NEXT = 'fndn', 128 EDIT_FIND_PREVIOUS = 'fndp', 129 FIND_TEXT_CHANGED = 'ftxt', 130 131 SELECT_TAB = 'sltb', 132 }; 133 134 135 static const int32 kModifiers = B_SHIFT_KEY | B_COMMAND_KEY 136 | B_CONTROL_KEY | B_OPTION_KEY | B_MENU_KEY; 137 138 139 static const char* kHandledProtocols[] = { 140 "http", 141 "https", 142 "file", 143 "about", 144 "data", 145 "gopher" 146 }; 147 148 149 static BLayoutItem* 150 layoutItemFor(BView* view) 151 { 152 BLayout* layout = view->Parent()->GetLayout(); 153 int32 index = layout->IndexOfView(view); 154 return layout->ItemAt(index); 155 } 156 157 158 class BookmarkMenu : public BNavMenu { 159 public: 160 BookmarkMenu(const char* title, BHandler* target, const entry_ref* navDir) 161 : 162 BNavMenu(title, B_REFS_RECEIVED, target) 163 { 164 // Add these items here already, so the shortcuts work even when 165 // the menu has never been opened yet. 166 _AddStaticItems(); 167 168 SetNavDir(navDir); 169 } 170 171 virtual void AttachedToWindow() 172 { 173 RemoveItems(0, CountItems(), true); 174 ForceRebuild(); 175 BNavMenu::AttachedToWindow(); 176 if (CountItems() > 0) 177 AddItem(new BSeparatorItem(), 0); 178 _AddStaticItems(); 179 DoLayout(); 180 } 181 182 private: 183 void _AddStaticItems() 184 { 185 AddItem(new BMenuItem(B_TRANSLATE("Manage bookmarks"), 186 new BMessage(SHOW_BOOKMARKS), 'M'), 0); 187 AddItem(new BMenuItem(B_TRANSLATE("Bookmark this page"), 188 new BMessage(CREATE_BOOKMARK), 'B'), 0); 189 } 190 }; 191 192 193 class PageUserData : public BWebView::UserData { 194 public: 195 PageUserData(BView* focusedView) 196 : 197 fFocusedView(focusedView), 198 fPageIcon(NULL), 199 fURLInputSelectionStart(-1), 200 fURLInputSelectionEnd(-1) 201 { 202 } 203 204 ~PageUserData() 205 { 206 delete fPageIcon; 207 } 208 209 void SetFocusedView(BView* focusedView) 210 { 211 fFocusedView = focusedView; 212 } 213 214 BView* FocusedView() const 215 { 216 return fFocusedView; 217 } 218 219 void SetPageIcon(const BBitmap* icon) 220 { 221 delete fPageIcon; 222 if (icon) 223 fPageIcon = new BBitmap(icon); 224 else 225 fPageIcon = NULL; 226 } 227 228 const BBitmap* PageIcon() const 229 { 230 return fPageIcon; 231 } 232 233 void SetURLInputContents(const char* text) 234 { 235 fURLInputContents = text; 236 } 237 238 const BString& URLInputContents() const 239 { 240 return fURLInputContents; 241 } 242 243 void SetURLInputSelection(int32 selectionStart, int32 selectionEnd) 244 { 245 fURLInputSelectionStart = selectionStart; 246 fURLInputSelectionEnd = selectionEnd; 247 } 248 249 int32 URLInputSelectionStart() const 250 { 251 return fURLInputSelectionStart; 252 } 253 254 int32 URLInputSelectionEnd() const 255 { 256 return fURLInputSelectionEnd; 257 } 258 259 private: 260 BView* fFocusedView; 261 BBitmap* fPageIcon; 262 BString fURLInputContents; 263 int32 fURLInputSelectionStart; 264 int32 fURLInputSelectionEnd; 265 }; 266 267 268 class CloseButton : public BButton { 269 public: 270 CloseButton(BMessage* message) 271 : 272 BButton("close button", NULL, message), 273 fOverCloseRect(false) 274 { 275 // Button is 16x16 regardless of font size 276 SetExplicitMinSize(BSize(15, 15)); 277 SetExplicitMaxSize(BSize(15, 15)); 278 } 279 280 virtual void Draw(BRect updateRect) 281 { 282 BRect frame = Bounds(); 283 BRect closeRect(frame.InsetByCopy(4, 4)); 284 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 285 float tint = B_DARKEN_1_TINT; 286 287 if (fOverCloseRect) 288 tint *= 1.4; 289 else 290 tint *= 1.2; 291 292 if (Value() == B_CONTROL_ON && fOverCloseRect) { 293 // Draw the button frame 294 be_control_look->DrawButtonFrame(this, frame, updateRect, 295 base, base, BControlLook::B_ACTIVATED 296 | BControlLook::B_BLEND_FRAME); 297 be_control_look->DrawButtonBackground(this, frame, 298 updateRect, base, BControlLook::B_ACTIVATED); 299 closeRect.OffsetBy(1, 1); 300 tint *= 1.2; 301 } else { 302 SetHighColor(base); 303 FillRect(updateRect); 304 } 305 306 // Draw the × 307 base = tint_color(base, tint); 308 SetHighColor(base); 309 SetPenSize(2); 310 StrokeLine(closeRect.LeftTop(), closeRect.RightBottom()); 311 StrokeLine(closeRect.LeftBottom(), closeRect.RightTop()); 312 SetPenSize(1); 313 } 314 315 virtual void MouseMoved(BPoint where, uint32 transit, 316 const BMessage* dragMessage) 317 { 318 switch (transit) { 319 case B_ENTERED_VIEW: 320 fOverCloseRect = true; 321 Invalidate(); 322 break; 323 case B_EXITED_VIEW: 324 fOverCloseRect = false; 325 Invalidate(); 326 break; 327 case B_INSIDE_VIEW: 328 fOverCloseRect = true; 329 break; 330 case B_OUTSIDE_VIEW: 331 fOverCloseRect = false; 332 break; 333 } 334 335 BButton::MouseMoved(where, transit, dragMessage); 336 } 337 338 private: 339 bool fOverCloseRect; 340 }; 341 342 343 // #pragma mark - BrowserWindow 344 345 346 BrowserWindow::BrowserWindow(BRect frame, SettingsMessage* appSettings, 347 const BString& url, BUrlContext* context, uint32 interfaceElements, 348 BWebView* webView) 349 : 350 BWebWindow(frame, kApplicationName, 351 B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 352 B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS), 353 fIsFullscreen(false), 354 fInterfaceVisible(false), 355 fMenusRunning(false), 356 fPulseRunner(NULL), 357 fVisibleInterfaceElements(interfaceElements), 358 fContext(context), 359 fAppSettings(appSettings), 360 fZoomTextOnly(true), 361 fShowTabsIfSinglePageOpen(true), 362 fAutoHideInterfaceInFullscreenMode(false), 363 fAutoHidePointer(false), 364 fBookmarkBar(NULL) 365 { 366 // Begin listening to settings changes and read some current values. 367 fAppSettings->AddListener(BMessenger(this)); 368 // fZoomTextOnly = fAppSettings->GetValue("zoom text only", fZoomTextOnly); 369 fShowTabsIfSinglePageOpen = fAppSettings->GetValue( 370 kSettingsKeyShowTabsIfSinglePageOpen, fShowTabsIfSinglePageOpen); 371 372 fAutoHidePointer = fAppSettings->GetValue(kSettingsKeyAutoHidePointer, 373 fAutoHidePointer); 374 375 fNewWindowPolicy = fAppSettings->GetValue(kSettingsKeyNewWindowPolicy, 376 (uint32)OpenStartPage); 377 fNewTabPolicy = fAppSettings->GetValue(kSettingsKeyNewTabPolicy, 378 (uint32)OpenBlankPage); 379 fStartPageURL = fAppSettings->GetValue(kSettingsKeyStartPageURL, 380 kDefaultStartPageURL); 381 fSearchPageURL = fAppSettings->GetValue(kSettingsKeySearchPageURL, 382 kDefaultSearchPageURL); 383 384 // Create the interface elements 385 BMessage* newTabMessage = new BMessage(NEW_TAB); 386 newTabMessage->AddString("url", ""); 387 newTabMessage->AddPointer("window", this); 388 newTabMessage->AddBool("select", true); 389 fTabManager = new TabManager(BMessenger(this), newTabMessage); 390 391 // Menu 392 #if INTEGRATE_MENU_INTO_TAB_BAR 393 BMenu* mainMenu = fTabManager->Menu(); 394 #else 395 BMenu* mainMenu = new BMenuBar("Main menu"); 396 #endif 397 BMenu* menu = new BMenu(B_TRANSLATE("Window")); 398 BMessage* newWindowMessage = new BMessage(NEW_WINDOW); 399 newWindowMessage->AddString("url", ""); 400 BMenuItem* newItem = new BMenuItem(B_TRANSLATE("New window"), 401 newWindowMessage, 'N'); 402 menu->AddItem(newItem); 403 newItem->SetTarget(be_app); 404 newItem = new BMenuItem(B_TRANSLATE("New tab"), 405 new BMessage(*newTabMessage), 'T'); 406 menu->AddItem(newItem); 407 newItem->SetTarget(be_app); 408 menu->AddItem(new BMenuItem(B_TRANSLATE("Open location"), 409 new BMessage(OPEN_LOCATION), 'L')); 410 menu->AddSeparatorItem(); 411 menu->AddItem(new BMenuItem(B_TRANSLATE("Close window"), 412 new BMessage(B_QUIT_REQUESTED), 'W', B_SHIFT_KEY)); 413 menu->AddItem(new BMenuItem(B_TRANSLATE("Close tab"), 414 new BMessage(CLOSE_TAB), 'W')); 415 menu->AddItem(new BMenuItem(B_TRANSLATE("Save page as" B_UTF8_ELLIPSIS), 416 new BMessage(SAVE_PAGE), 'S')); 417 menu->AddSeparatorItem(); 418 menu->AddItem(new BMenuItem(B_TRANSLATE("Downloads"), 419 new BMessage(SHOW_DOWNLOAD_WINDOW), 'D')); 420 menu->AddItem(new BMenuItem(B_TRANSLATE("Settings"), 421 new BMessage(SHOW_SETTINGS_WINDOW))); 422 menu->AddItem(new BMenuItem(B_TRANSLATE("Cookie manager"), 423 new BMessage(SHOW_COOKIE_WINDOW))); 424 menu->AddItem(new BMenuItem(B_TRANSLATE("Script console"), 425 new BMessage(SHOW_CONSOLE_WINDOW))); 426 BMenuItem* aboutItem = new BMenuItem(B_TRANSLATE("About"), 427 new BMessage(B_ABOUT_REQUESTED)); 428 menu->AddItem(aboutItem); 429 aboutItem->SetTarget(be_app); 430 menu->AddSeparatorItem(); 431 BMenuItem* quitItem = new BMenuItem(B_TRANSLATE("Quit"), 432 new BMessage(B_QUIT_REQUESTED), 'Q'); 433 menu->AddItem(quitItem); 434 quitItem->SetTarget(be_app); 435 mainMenu->AddItem(menu); 436 437 menu = new BMenu(B_TRANSLATE("Edit")); 438 menu->AddItem(fCutMenuItem = new BMenuItem(B_TRANSLATE("Cut"), 439 new BMessage(B_CUT), 'X')); 440 menu->AddItem(fCopyMenuItem = new BMenuItem(B_TRANSLATE("Copy"), 441 new BMessage(B_COPY), 'C')); 442 menu->AddItem(fPasteMenuItem = new BMenuItem(B_TRANSLATE("Paste"), 443 new BMessage(B_PASTE), 'V')); 444 menu->AddSeparatorItem(); 445 menu->AddItem(new BMenuItem(B_TRANSLATE("Find"), 446 new BMessage(EDIT_SHOW_FIND_GROUP), 'F')); 447 menu->AddItem(fFindPreviousMenuItem 448 = new BMenuItem(B_TRANSLATE("Find previous"), 449 new BMessage(EDIT_FIND_PREVIOUS), 'G', B_SHIFT_KEY)); 450 menu->AddItem(fFindNextMenuItem = new BMenuItem(B_TRANSLATE("Find next"), 451 new BMessage(EDIT_FIND_NEXT), 'G')); 452 mainMenu->AddItem(menu); 453 fFindPreviousMenuItem->SetEnabled(false); 454 fFindNextMenuItem->SetEnabled(false); 455 456 menu = new BMenu(B_TRANSLATE("View")); 457 menu->AddItem(new BMenuItem(B_TRANSLATE("Reload"), new BMessage(RELOAD), 458 'R')); 459 // the label will be replaced with the appropriate text later on 460 fBookmarkBarMenuItem = new BMenuItem(B_TRANSLATE("Show bookmark bar"), 461 new BMessage(SHOW_HIDE_BOOKMARK_BAR)); 462 menu->AddItem(fBookmarkBarMenuItem); 463 menu->AddSeparatorItem(); 464 menu->AddItem(new BMenuItem(B_TRANSLATE("Increase size"), 465 new BMessage(ZOOM_FACTOR_INCREASE), '+')); 466 menu->AddItem(new BMenuItem(B_TRANSLATE("Decrease size"), 467 new BMessage(ZOOM_FACTOR_DECREASE), '-')); 468 menu->AddItem(new BMenuItem(B_TRANSLATE("Reset size"), 469 new BMessage(ZOOM_FACTOR_RESET), '0')); 470 fZoomTextOnlyMenuItem = new BMenuItem(B_TRANSLATE("Zoom text only"), 471 new BMessage(ZOOM_TEXT_ONLY)); 472 fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly); 473 menu->AddItem(fZoomTextOnlyMenuItem); 474 475 menu->AddSeparatorItem(); 476 fFullscreenItem = new BMenuItem(B_TRANSLATE("Full screen"), 477 new BMessage(TOGGLE_FULLSCREEN), B_RETURN); 478 menu->AddItem(fFullscreenItem); 479 menu->AddItem(new BMenuItem(B_TRANSLATE("Page source"), 480 new BMessage(SHOW_PAGE_SOURCE), 'U')); 481 mainMenu->AddItem(menu); 482 483 fHistoryMenu = new BMenu(B_TRANSLATE("History")); 484 fHistoryMenu->AddItem(fBackMenuItem = new BMenuItem(B_TRANSLATE("Back"), 485 new BMessage(GO_BACK), B_LEFT_ARROW)); 486 fHistoryMenu->AddItem(fForwardMenuItem 487 = new BMenuItem(B_TRANSLATE("Forward"), new BMessage(GO_FORWARD), 488 B_RIGHT_ARROW)); 489 fHistoryMenu->AddSeparatorItem(); 490 fHistoryMenuFixedItemCount = fHistoryMenu->CountItems(); 491 mainMenu->AddItem(fHistoryMenu); 492 493 BPath bookmarkPath; 494 entry_ref bookmarkRef; 495 if (_BookmarkPath(bookmarkPath) == B_OK 496 && get_ref_for_path(bookmarkPath.Path(), &bookmarkRef) == B_OK) { 497 BMenu* bookmarkMenu 498 = new BookmarkMenu(B_TRANSLATE("Bookmarks"), this, &bookmarkRef); 499 mainMenu->AddItem(bookmarkMenu); 500 501 BDirectory barDir(&bookmarkRef); 502 BEntry bookmarkBar(&barDir, "Bookmark bar"); 503 entry_ref bookmarkBarRef; 504 // TODO we could also check if the folder is empty here. 505 if (bookmarkBar.Exists() && bookmarkBar.GetRef(&bookmarkBarRef) == B_OK) { 506 fBookmarkBar = new BookmarkBar("Bookmarks", this, &bookmarkBarRef); 507 fBookmarkBarMenuItem->SetEnabled(true); 508 } else 509 fBookmarkBarMenuItem->SetEnabled(false); 510 } 511 512 // Back, Forward, Stop & Home buttons 513 fBackButton = new BIconButton("Back", NULL, new BMessage(GO_BACK)); 514 fBackButton->SetIcon(201); 515 fBackButton->TrimIcon(); 516 517 fForwardButton = new BIconButton("Forward", NULL, new BMessage(GO_FORWARD)); 518 fForwardButton->SetIcon(202); 519 fForwardButton->TrimIcon(); 520 521 fStopButton = new BIconButton("Stop", NULL, new BMessage(STOP)); 522 fStopButton->SetIcon(204); 523 fStopButton->TrimIcon(); 524 525 fHomeButton = new BIconButton("Home", NULL, new BMessage(HOME)); 526 fHomeButton->SetIcon(206); 527 fHomeButton->TrimIcon(); 528 if (!fAppSettings->GetValue(kSettingsKeyShowHomeButton, true)) 529 fHomeButton->Hide(); 530 531 // URL input group 532 fURLInputGroup = new URLInputGroup(new BMessage(GOTO_URL)); 533 534 // Status Bar 535 fStatusText = new BStringView("status", ""); 536 fStatusText->SetAlignment(B_ALIGN_LEFT); 537 fStatusText->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 538 fStatusText->SetExplicitMinSize(BSize(150, 12)); 539 // Prevent the window from growing to fit a long status message... 540 BFont font(be_plain_font); 541 font.SetSize(ceilf(font.Size() * 0.8)); 542 fStatusText->SetFont(&font, B_FONT_SIZE); 543 544 // Loading progress bar 545 fLoadingProgressBar = new BStatusBar("progress"); 546 fLoadingProgressBar->SetMaxValue(100); 547 fLoadingProgressBar->Hide(); 548 fLoadingProgressBar->SetBarHeight(12); 549 550 const float kInsetSpacing = 3; 551 const float kElementSpacing = 5; 552 553 // Find group 554 fFindCloseButton = new CloseButton(new BMessage(EDIT_HIDE_FIND_GROUP)); 555 fFindTextControl = new BTextControl("find", B_TRANSLATE("Find:"), "", NULL); 556 fFindTextControl->SetModificationMessage(new BMessage(FIND_TEXT_CHANGED)); 557 fFindPreviousButton = new BButton(B_TRANSLATE("Previous"), 558 new BMessage(EDIT_FIND_PREVIOUS)); 559 fFindPreviousButton->SetToolTip( 560 B_TRANSLATE_COMMENT("Find previous occurrence of search terms", 561 "find bar previous button tooltip")); 562 fFindNextButton = new BButton(B_TRANSLATE("Next"), 563 new BMessage(EDIT_FIND_NEXT)); 564 fFindNextButton->SetToolTip( 565 B_TRANSLATE_COMMENT("Find next occurrence of search terms", 566 "find bar next button tooltip")); 567 fFindCaseSensitiveCheckBox = new BCheckBox(B_TRANSLATE("Match case")); 568 BGroupLayout* findGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) 569 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 570 .Add(BGroupLayoutBuilder(B_HORIZONTAL, B_USE_SMALL_SPACING) 571 .Add(fFindCloseButton) 572 .Add(fFindTextControl) 573 .Add(fFindPreviousButton) 574 .Add(fFindNextButton) 575 .Add(fFindCaseSensitiveCheckBox) 576 .SetInsets(kInsetSpacing, kInsetSpacing, 577 kInsetSpacing, kInsetSpacing) 578 ) 579 ; 580 581 // Navigation group 582 BGroupLayout* navigationGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) 583 .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing) 584 .Add(fBackButton) 585 .Add(fForwardButton) 586 .Add(fStopButton) 587 .Add(fHomeButton) 588 .Add(fURLInputGroup) 589 .SetInsets(kInsetSpacing, kInsetSpacing, kInsetSpacing, 590 kInsetSpacing) 591 ) 592 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 593 ; 594 595 // Status bar group 596 BGroupLayout* statusGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) 597 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 598 .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing) 599 .Add(fStatusText) 600 .Add(fLoadingProgressBar, 0.2) 601 .AddStrut(12 - kElementSpacing) 602 .SetInsets(kInsetSpacing, 0, kInsetSpacing, 0) 603 ) 604 ; 605 606 BitmapButton* toggleFullscreenButton = new BitmapButton(kWindowIconBits, 607 kWindowIconWidth, kWindowIconHeight, kWindowIconFormat, 608 new BMessage(TOGGLE_FULLSCREEN)); 609 toggleFullscreenButton->SetBackgroundMode(BitmapButton::MENUBAR_BACKGROUND); 610 611 BGroupLayout* menuBarGroup = BLayoutBuilder::Group<>(B_HORIZONTAL, 0.0) 612 .Add(mainMenu) 613 .Add(toggleFullscreenButton, 0.0f) 614 ; 615 616 if (fAppSettings->GetValue(kSettingsShowBookmarkBar, true)) 617 _ShowBookmarkBar(true); 618 else 619 _ShowBookmarkBar(false); 620 621 fSavePanel = new BFilePanel(B_SAVE_PANEL, new BMessenger(this), NULL, 0, 622 false); 623 624 // Layout 625 BGroupView* topView = new BGroupView(B_VERTICAL, 0.0); 626 627 #if !INTEGRATE_MENU_INTO_TAB_BAR 628 topView->AddChild(menuBarGroup); 629 #endif 630 topView->AddChild(fTabManager->TabGroup()); 631 topView->AddChild(navigationGroup); 632 if (fBookmarkBar != NULL) 633 topView->AddChild(fBookmarkBar); 634 topView->AddChild(fTabManager->ContainerView()); 635 topView->AddChild(findGroup); 636 topView->AddChild(statusGroup); 637 638 AddChild(topView); 639 640 fURLInputGroup->MakeFocus(true); 641 642 fMenuGroup = menuBarGroup; 643 fTabGroup = fTabManager->TabGroup()->GetLayout(); 644 fNavigationGroup = navigationGroup; 645 fFindGroup = findGroup; 646 fStatusGroup = statusGroup; 647 fToggleFullscreenButton = layoutItemFor(toggleFullscreenButton); 648 649 fFindGroup->SetVisible(false); 650 fToggleFullscreenButton->SetVisible(false); 651 652 CreateNewTab(url, true, webView); 653 _ShowInterface(true); 654 _SetAutoHideInterfaceInFullscreen(fAppSettings->GetValue( 655 kSettingsKeyAutoHideInterfaceInFullscreenMode, 656 fAutoHideInterfaceInFullscreenMode)); 657 658 AddShortcut('F', B_COMMAND_KEY | B_SHIFT_KEY, 659 new BMessage(EDIT_HIDE_FIND_GROUP)); 660 // TODO: Should be a different shortcut, H is usually for Find selection. 661 AddShortcut('H', B_COMMAND_KEY, new BMessage(HOME)); 662 663 // Add shortcuts to select a particular tab 664 for (int32 i = 1; i <= 9; i++) { 665 BMessage* selectTab = new BMessage(SELECT_TAB); 666 selectTab->AddInt32("tab index", i - 1); 667 char numStr[2]; 668 snprintf(numStr, sizeof(numStr), "%d", (int) i); 669 AddShortcut(numStr[0], B_COMMAND_KEY, selectTab); 670 } 671 672 BKeymap keymap; 673 keymap.SetToCurrent(); 674 BObjectList<const char> unmodified(3, true); 675 if (keymap.GetModifiedCharacters("+", B_SHIFT_KEY, 0, &unmodified) 676 == B_OK) { 677 int32 count = unmodified.CountItems(); 678 for (int32 i = 0; i < count; i++) { 679 uint32 key = BUnicodeChar::FromUTF8(unmodified.ItemAt(i)); 680 if (!HasShortcut(key, 0)) { 681 // Add semantic zoom in shortcut, bug #7428 682 AddShortcut(key, B_COMMAND_KEY, 683 new BMessage(ZOOM_FACTOR_INCREASE)); 684 } 685 } 686 } 687 unmodified.MakeEmpty(); 688 689 be_app->PostMessage(WINDOW_OPENED); 690 } 691 692 693 BrowserWindow::~BrowserWindow() 694 { 695 fAppSettings->RemoveListener(BMessenger(this)); 696 delete fTabManager; 697 delete fPulseRunner; 698 delete fSavePanel; 699 } 700 701 702 void 703 BrowserWindow::DispatchMessage(BMessage* message, BHandler* target) 704 { 705 const char* bytes; 706 int32 modifierKeys; 707 if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN) 708 && message->FindString("bytes", &bytes) == B_OK 709 && message->FindInt32("modifiers", &modifierKeys) == B_OK) { 710 modifierKeys = (int32)((uint32)modifierKeys & kModifiers); 711 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus()); 712 if (bytes[0] == B_LEFT_ARROW && modifierKeys == B_COMMAND_KEY) { 713 if (textView != NULL) 714 textView->KeyDown(bytes, modifierKeys); 715 else 716 PostMessage(GO_BACK); 717 return; 718 } else if (bytes[0] == B_RIGHT_ARROW && modifierKeys == B_COMMAND_KEY) { 719 if (textView != NULL) 720 textView->KeyDown(bytes, modifierKeys); 721 else 722 PostMessage(GO_FORWARD); 723 return; 724 } else if (bytes[0] == B_FUNCTION_KEY) { 725 // Some function key Firefox compatibility 726 int32 key; 727 if (message->FindInt32("key", &key) == B_OK) { 728 switch (key) { 729 case B_F5_KEY: 730 PostMessage(RELOAD); 731 break; 732 case B_F11_KEY: 733 PostMessage(TOGGLE_FULLSCREEN); 734 break; 735 default: 736 break; 737 } 738 } 739 } else if (target == fURLInputGroup->TextView()) { 740 // Handle B_RETURN in the URL text control. This is the easiest 741 // way to react *only* when the user presses the return key in the 742 // address bar, as opposed to trying to load whatever is in there 743 // when the text control just goes out of focus. 744 if (bytes[0] == B_RETURN) { 745 // Do it in such a way that the user sees the Go-button go down. 746 _InvokeButtonVisibly(fURLInputGroup->GoButton()); 747 return; 748 } 749 } else if (target == fFindTextControl->TextView()) { 750 // Handle B_RETURN when the find text control has focus. 751 if (bytes[0] == B_RETURN) { 752 if ((modifierKeys & B_SHIFT_KEY) != 0) 753 _InvokeButtonVisibly(fFindPreviousButton); 754 else 755 _InvokeButtonVisibly(fFindNextButton); 756 return; 757 } else if (bytes[0] == B_ESCAPE) { 758 _InvokeButtonVisibly(fFindCloseButton); 759 return; 760 } 761 } else if (bytes[0] == B_ESCAPE && !fMenusRunning) { 762 if (modifierKeys == B_COMMAND_KEY) 763 _ShowInterface(true); 764 else { 765 // Default escape key behavior: 766 PostMessage(STOP); 767 return; 768 } 769 } 770 } 771 772 if (message->what == B_MOUSE_MOVED || message->what == B_MOUSE_DOWN 773 || message->what == B_MOUSE_UP) { 774 message->FindPoint("where", &fLastMousePos); 775 if (message->FindInt64("when", &fLastMouseMovedTime) != B_OK) 776 fLastMouseMovedTime = system_time(); 777 _CheckAutoHideInterface(); 778 } 779 780 if (message->what == B_MOUSE_WHEEL_CHANGED) { 781 BPoint where; 782 uint32 buttons; 783 CurrentWebView()->GetMouse(&where, &buttons, false); 784 // Only do this when the mouse is over the web view 785 if (CurrentWebView()->Bounds().Contains(where)) { 786 // Zoom and unzoom text on Command + mouse wheel. 787 // This could of course (and maybe should be) implemented in the 788 // WebView, but there would need to be a way for the WebView to 789 // know the setting of the fZoomTextOnly member here. Plus other 790 // clients of the API may not want this feature. 791 if ((modifiers() & B_COMMAND_KEY) != 0) { 792 float deltaY; 793 if (message->FindFloat("be:wheel_delta_y", &deltaY) == B_OK) { 794 if (deltaY < 0) 795 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly); 796 else 797 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly); 798 799 return; 800 } 801 } 802 } else { 803 // Also don't scroll up and down if the mouse is not over the 804 // web view 805 return; 806 } 807 } 808 809 BWebWindow::DispatchMessage(message, target); 810 } 811 812 813 void 814 BrowserWindow::MessageReceived(BMessage* message) 815 { 816 switch (message->what) { 817 case OPEN_LOCATION: 818 _ShowInterface(true); 819 if (fURLInputGroup->TextView()->IsFocus()) 820 fURLInputGroup->TextView()->SelectAll(); 821 else 822 fURLInputGroup->MakeFocus(true); 823 break; 824 825 case RELOAD: 826 CurrentWebView()->Reload(); 827 break; 828 829 case SHOW_HIDE_BOOKMARK_BAR: 830 _ShowBookmarkBar(fBookmarkBar->IsHidden()); 831 break; 832 833 case GOTO_URL: 834 { 835 BString url; 836 if (message->FindString("url", &url) != B_OK) 837 url = fURLInputGroup->Text(); 838 839 _SetPageIcon(CurrentWebView(), NULL); 840 _SmartURLHandler(url); 841 842 break; 843 } 844 845 case SAVE_PAGE: 846 { 847 fSavePanel->SetSaveText(CurrentWebView()->MainFrameTitle()); 848 fSavePanel->Show(); 849 break; 850 } 851 852 case B_SAVE_REQUESTED: 853 { 854 entry_ref ref; 855 BString name; 856 857 if (message->FindRef("directory", &ref) == B_OK && 858 message->FindString("name", &name) == B_OK) { 859 BDirectory dir(&ref); 860 BFile output(&dir, name, 861 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 862 CurrentWebView()->WebPage()->GetContentsAsMHTML(output); 863 } 864 865 break; 866 } 867 868 case GO_BACK: 869 CurrentWebView()->GoBack(); 870 break; 871 872 case GO_FORWARD: 873 CurrentWebView()->GoForward(); 874 break; 875 876 case STOP: 877 CurrentWebView()->StopLoading(); 878 break; 879 880 case HOME: 881 CurrentWebView()->LoadURL(fStartPageURL); 882 break; 883 884 case CLEAR_HISTORY: { 885 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 886 if (history->CountItems() == 0) 887 break; 888 BAlert* alert = new BAlert(B_TRANSLATE("Confirmation"), 889 B_TRANSLATE("Do you really want to " 890 "clear the browsing history?"), B_TRANSLATE("Clear"), 891 B_TRANSLATE("Cancel")); 892 alert->SetShortcut(1, B_ESCAPE); 893 894 if (alert->Go() == 0) 895 history->Clear(); 896 break; 897 } 898 899 case CREATE_BOOKMARK: 900 _CreateBookmark(); 901 break; 902 903 case SHOW_BOOKMARKS: 904 _ShowBookmarks(); 905 break; 906 907 case B_REFS_RECEIVED: 908 { 909 // Currently the only source of these messages is the bookmarks 910 // menu. 911 // Filter refs into URLs, this also gets rid of refs for folders. 912 // For clicks on sub-folders in the bookmarks menu, we have Tracker 913 // open the corresponding folder. 914 entry_ref ref; 915 uint32 addedCount = 0; 916 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 917 BEntry entry(&ref); 918 uint32 addedSubCount = 0; 919 if (entry.IsDirectory()) { 920 BDirectory directory(&entry); 921 _AddBookmarkURLsRecursively(directory, message, 922 addedSubCount); 923 } else { 924 BFile file(&ref, B_READ_ONLY); 925 BString url; 926 if (_ReadURLAttr(file, url)) { 927 message->AddString("url", url.String()); 928 addedSubCount++; 929 } 930 } 931 if (addedSubCount == 0) { 932 // Don't know what to do with this entry, just pass it 933 // on to the system to handle. Note that this may result 934 // in us opening other supported files via the application 935 // mechanism. 936 be_roster->Launch(&ref); 937 } 938 addedCount += addedSubCount; 939 } 940 message->RemoveName("refs"); 941 if (addedCount > 10) { 942 BString string(B_TRANSLATE_COMMENT("Do you want to open " 943 "%addedCount bookmarks all at once?", "Don't translate " 944 "variable %addedCount.")); 945 string.ReplaceFirst("%addedCount", BString() << addedCount); 946 947 BAlert* alert = new BAlert( 948 B_TRANSLATE("Open bookmarks confirmation"), 949 string.String(), B_TRANSLATE("Cancel"), 950 B_TRANSLATE("Open all")); 951 alert->SetShortcut(0, B_ESCAPE); 952 if (alert->Go() == 0) 953 break; 954 } 955 message->AddPointer("window", this); 956 be_app->PostMessage(message); 957 break; 958 } 959 960 case B_SIMPLE_DATA: 961 { 962 // User possibly dropped files on this window. 963 // If there is more than one entry_ref, let the app handle it 964 // (open one new page per ref). If there is one ref, open it in 965 // this window. 966 type_code type; 967 int32 countFound; 968 if (message->GetInfo("refs", &type, &countFound) != B_OK 969 || type != B_REF_TYPE) { 970 break; 971 } 972 if (countFound > 1) { 973 message->what = B_REFS_RECEIVED; 974 be_app->PostMessage(message); 975 break; 976 } 977 entry_ref ref; 978 if (message->FindRef("refs", &ref) != B_OK) 979 break; 980 BEntry entry(&ref, true); 981 BPath path; 982 if (!entry.Exists() || entry.GetPath(&path) != B_OK) 983 break; 984 985 BUrl url(path); 986 CurrentWebView()->LoadURL(url); 987 break; 988 } 989 990 case ZOOM_FACTOR_INCREASE: 991 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly); 992 break; 993 case ZOOM_FACTOR_DECREASE: 994 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly); 995 break; 996 case ZOOM_FACTOR_RESET: 997 CurrentWebView()->ResetZoomFactor(); 998 break; 999 case ZOOM_TEXT_ONLY: 1000 fZoomTextOnly = !fZoomTextOnly; 1001 fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly); 1002 // TODO: Would be nice to have an instant update if the page is 1003 // already zoomed. 1004 break; 1005 1006 case TOGGLE_FULLSCREEN: 1007 ToggleFullscreen(); 1008 break; 1009 1010 case TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN: 1011 _SetAutoHideInterfaceInFullscreen( 1012 !fAutoHideInterfaceInFullscreenMode); 1013 break; 1014 1015 case CHECK_AUTO_HIDE_INTERFACE: 1016 _CheckAutoHideInterface(); 1017 break; 1018 1019 case SHOW_PAGE_SOURCE: 1020 CurrentWebView()->WebPage()->SendPageSource(); 1021 break; 1022 case B_PAGE_SOURCE_RESULT: 1023 _HandlePageSourceResult(message); 1024 break; 1025 1026 case EDIT_FIND_NEXT: 1027 CurrentWebView()->FindString(fFindTextControl->Text(), true, 1028 fFindCaseSensitiveCheckBox->Value()); 1029 break; 1030 case FIND_TEXT_CHANGED: 1031 { 1032 bool findTextAvailable = strlen(fFindTextControl->Text()) > 0; 1033 fFindPreviousMenuItem->SetEnabled(findTextAvailable); 1034 fFindNextMenuItem->SetEnabled(findTextAvailable); 1035 break; 1036 } 1037 case EDIT_FIND_PREVIOUS: 1038 CurrentWebView()->FindString(fFindTextControl->Text(), false, 1039 fFindCaseSensitiveCheckBox->Value()); 1040 break; 1041 case EDIT_SHOW_FIND_GROUP: 1042 if (!fFindGroup->IsVisible()) 1043 fFindGroup->SetVisible(true); 1044 fFindTextControl->MakeFocus(true); 1045 break; 1046 case EDIT_HIDE_FIND_GROUP: 1047 if (fFindGroup->IsVisible()) { 1048 fFindGroup->SetVisible(false); 1049 if (CurrentWebView() != NULL) 1050 CurrentWebView()->MakeFocus(true); 1051 } 1052 break; 1053 1054 case B_CUT: 1055 case B_COPY: 1056 case B_PASTE: 1057 { 1058 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus()); 1059 if (textView != NULL) 1060 textView->MessageReceived(message); 1061 else if (CurrentWebView() != NULL) 1062 CurrentWebView()->MessageReceived(message); 1063 break; 1064 } 1065 1066 case B_EDITING_CAPABILITIES_RESULT: 1067 { 1068 BWebView* webView; 1069 if (message->FindPointer("view", 1070 reinterpret_cast<void**>(&webView)) != B_OK 1071 || webView != CurrentWebView()) { 1072 break; 1073 } 1074 bool canCut; 1075 bool canCopy; 1076 bool canPaste; 1077 if (message->FindBool("can cut", &canCut) != B_OK) 1078 canCut = false; 1079 if (message->FindBool("can copy", &canCopy) != B_OK) 1080 canCopy = false; 1081 if (message->FindBool("can paste", &canPaste) != B_OK) 1082 canPaste = false; 1083 fCutMenuItem->SetEnabled(canCut); 1084 fCopyMenuItem->SetEnabled(canCopy); 1085 fPasteMenuItem->SetEnabled(canPaste); 1086 break; 1087 } 1088 1089 case SHOW_DOWNLOAD_WINDOW: 1090 case SHOW_SETTINGS_WINDOW: 1091 case SHOW_CONSOLE_WINDOW: 1092 case SHOW_COOKIE_WINDOW: 1093 message->AddUInt32("workspaces", Workspaces()); 1094 be_app->PostMessage(message); 1095 break; 1096 1097 case CLOSE_TAB: 1098 if (fTabManager->CountTabs() > 1) { 1099 int32 index; 1100 if (message->FindInt32("tab index", &index) != B_OK) 1101 index = fTabManager->SelectedTabIndex(); 1102 _ShutdownTab(index); 1103 _UpdateTabGroupVisibility(); 1104 } else 1105 PostMessage(B_QUIT_REQUESTED); 1106 break; 1107 1108 case SELECT_TAB: 1109 { 1110 int32 index; 1111 if (message->FindInt32("tab index", &index) == B_OK 1112 && fTabManager->SelectedTabIndex() != index 1113 && fTabManager->CountTabs() > index) { 1114 fTabManager->SelectTab(index); 1115 } 1116 1117 break; 1118 } 1119 1120 case TAB_CHANGED: 1121 { 1122 // This message may be received also when the last tab closed, 1123 // i.e. with index == -1. 1124 int32 index; 1125 if (message->FindInt32("tab index", &index) != B_OK) 1126 index = -1; 1127 _TabChanged(index); 1128 break; 1129 } 1130 1131 case SETTINGS_VALUE_CHANGED: 1132 { 1133 BString name; 1134 if (message->FindString("name", &name) != B_OK) 1135 break; 1136 bool flag; 1137 BString string; 1138 uint32 value; 1139 if (name == kSettingsKeyShowTabsIfSinglePageOpen 1140 && message->FindBool("value", &flag) == B_OK) { 1141 if (fShowTabsIfSinglePageOpen != flag) { 1142 fShowTabsIfSinglePageOpen = flag; 1143 _UpdateTabGroupVisibility(); 1144 } 1145 } else if (name == kSettingsKeyAutoHidePointer 1146 && message->FindBool("value", &flag) == B_OK) { 1147 fAutoHidePointer = flag; 1148 if (CurrentWebView()) 1149 CurrentWebView()->SetAutoHidePointer(fAutoHidePointer); 1150 } else if (name == kSettingsKeyStartPageURL 1151 && message->FindString("value", &string) == B_OK) { 1152 fStartPageURL = string; 1153 } else if (name == kSettingsKeySearchPageURL 1154 && message->FindString("value", &string) == B_OK) { 1155 fSearchPageURL = string; 1156 } else if (name == kSettingsKeyNewWindowPolicy 1157 && message->FindUInt32("value", &value) == B_OK) { 1158 fNewWindowPolicy = value; 1159 } else if (name == kSettingsKeyNewTabPolicy 1160 && message->FindUInt32("value", &value) == B_OK) { 1161 fNewTabPolicy = value; 1162 } else if (name == kSettingsKeyAutoHideInterfaceInFullscreenMode 1163 && message->FindBool("value", &flag) == B_OK) { 1164 _SetAutoHideInterfaceInFullscreen(flag); 1165 } else if (name == kSettingsKeyShowHomeButton 1166 && message->FindBool("value", &flag) == B_OK) { 1167 if (flag) 1168 fHomeButton->Show(); 1169 else 1170 fHomeButton->Hide(); 1171 } else if (name == kSettingsShowBookmarkBar 1172 && message->FindBool("value", &flag) == B_OK) { 1173 _ShowBookmarkBar(flag); 1174 } 1175 break; 1176 } 1177 case ADD_CONSOLE_MESSAGE: 1178 be_app->PostMessage(message); 1179 BWebWindow::MessageReceived(message); 1180 break; 1181 1182 default: 1183 BWebWindow::MessageReceived(message); 1184 break; 1185 } 1186 } 1187 1188 1189 status_t 1190 BrowserWindow::Archive(BMessage* archive, bool deep) const 1191 { 1192 status_t ret = archive->AddRect("window frame", Frame()); 1193 1194 for (int i = 0; i < fTabManager->CountTabs(); i++) { 1195 BWebView* view = dynamic_cast<BWebView*>(fTabManager->ViewForTab(i)); 1196 if (view == NULL) { 1197 continue; 1198 } 1199 1200 if (ret == B_OK) 1201 ret = archive->AddString("tab", view->MainFrameURL()); 1202 } 1203 1204 return ret; 1205 } 1206 1207 1208 bool 1209 BrowserWindow::QuitRequested() 1210 { 1211 // TODO: Check for modified form data and ask user for confirmation, etc. 1212 1213 BMessage message(WINDOW_CLOSED); 1214 Archive(&message); 1215 1216 // Iterate over all tabs to delete all BWebViews. 1217 // Do this here, so WebKit tear down happens earlier. 1218 SetCurrentWebView(NULL); 1219 while (fTabManager->CountTabs() > 0) 1220 _ShutdownTab(0); 1221 1222 message.AddRect("window frame", WindowFrame()); 1223 be_app->PostMessage(&message); 1224 return true; 1225 } 1226 1227 1228 void 1229 BrowserWindow::MenusBeginning() 1230 { 1231 _UpdateHistoryMenu(); 1232 _UpdateClipboardItems(); 1233 fMenusRunning = true; 1234 } 1235 1236 1237 void 1238 BrowserWindow::MenusEnded() 1239 { 1240 fMenusRunning = false; 1241 } 1242 1243 1244 void 1245 BrowserWindow::ScreenChanged(BRect screenSize, color_space format) 1246 { 1247 if (fIsFullscreen) 1248 _ResizeToScreen(); 1249 } 1250 1251 1252 void 1253 BrowserWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 1254 { 1255 if (fIsFullscreen) 1256 _ResizeToScreen(); 1257 } 1258 1259 1260 static bool 1261 viewIsChild(const BView* parent, const BView* view) 1262 { 1263 if (parent == view) 1264 return true; 1265 1266 int32 count = parent->CountChildren(); 1267 for (int32 i = 0; i < count; i++) { 1268 BView* child = parent->ChildAt(i); 1269 if (viewIsChild(child, view)) 1270 return true; 1271 } 1272 return false; 1273 } 1274 1275 1276 void 1277 BrowserWindow::SetCurrentWebView(BWebView* webView) 1278 { 1279 if (webView == CurrentWebView()) 1280 return; 1281 1282 if (CurrentWebView() != NULL) { 1283 // Remember the currently focused view before switching tabs, 1284 // so that we can revert the focus when switching back to this tab 1285 // later. 1286 PageUserData* userData = static_cast<PageUserData*>( 1287 CurrentWebView()->GetUserData()); 1288 if (userData == NULL) { 1289 userData = new PageUserData(CurrentFocus()); 1290 CurrentWebView()->SetUserData(userData); 1291 } 1292 userData->SetFocusedView(CurrentFocus()); 1293 userData->SetURLInputContents(fURLInputGroup->Text()); 1294 int32 selectionStart; 1295 int32 selectionEnd; 1296 fURLInputGroup->TextView()->GetSelection(&selectionStart, 1297 &selectionEnd); 1298 userData->SetURLInputSelection(selectionStart, selectionEnd); 1299 } 1300 1301 BWebWindow::SetCurrentWebView(webView); 1302 1303 if (webView != NULL) { 1304 webView->SetAutoHidePointer(fAutoHidePointer); 1305 1306 _UpdateTitle(webView->MainFrameTitle()); 1307 1308 // Restore the previous focus or focus the web view. 1309 PageUserData* userData = static_cast<PageUserData*>( 1310 webView->GetUserData()); 1311 BView* focusedView = NULL; 1312 if (userData != NULL) 1313 focusedView = userData->FocusedView(); 1314 1315 if (focusedView != NULL 1316 && viewIsChild(GetLayout()->View(), focusedView)) { 1317 focusedView->MakeFocus(true); 1318 } else 1319 webView->MakeFocus(true); 1320 1321 if (userData != NULL) { 1322 fURLInputGroup->SetPageIcon(userData->PageIcon()); 1323 if (userData->URLInputContents().Length()) 1324 fURLInputGroup->SetText(userData->URLInputContents()); 1325 else 1326 fURLInputGroup->SetText(webView->MainFrameURL()); 1327 if (userData->URLInputSelectionStart() >= 0) { 1328 fURLInputGroup->TextView()->Select( 1329 userData->URLInputSelectionStart(), 1330 userData->URLInputSelectionEnd()); 1331 } 1332 } else { 1333 fURLInputGroup->SetPageIcon(NULL); 1334 fURLInputGroup->SetText(webView->MainFrameURL()); 1335 } 1336 1337 // Trigger update of the interface to the new page, by requesting 1338 // to resend all notifications. 1339 webView->WebPage()->ResendNotifications(); 1340 } else 1341 _UpdateTitle(""); 1342 } 1343 1344 1345 bool 1346 BrowserWindow::IsBlankTab() const 1347 { 1348 if (CurrentWebView() == NULL) 1349 return false; 1350 BString requestedURL = CurrentWebView()->MainFrameRequestedURL(); 1351 return requestedURL.Length() == 0 1352 || requestedURL == _NewTabURL(fTabManager->CountTabs() == 1); 1353 } 1354 1355 1356 void 1357 BrowserWindow::CreateNewTab(const BString& _url, bool select, 1358 BWebView* webView) 1359 { 1360 bool applyNewPagePolicy = webView == NULL; 1361 // Executed in app thread (new BWebPage needs to be created in app thread). 1362 if (webView == NULL) 1363 webView = new BWebView("web view", fContext); 1364 1365 bool isNewWindow = fTabManager->CountTabs() == 0; 1366 1367 fTabManager->AddTab(webView, B_TRANSLATE("New tab")); 1368 1369 BString url(_url); 1370 if (applyNewPagePolicy && url.Length() == 0) 1371 url = _NewTabURL(isNewWindow); 1372 1373 if (url.Length() > 0) 1374 webView->LoadURL(url.String()); 1375 1376 if (select) { 1377 fTabManager->SelectTab(fTabManager->CountTabs() - 1); 1378 SetCurrentWebView(webView); 1379 webView->WebPage()->ResendNotifications(); 1380 fURLInputGroup->SetPageIcon(NULL); 1381 fURLInputGroup->SetText(url.String()); 1382 fURLInputGroup->MakeFocus(true); 1383 } 1384 1385 _ShowInterface(true); 1386 _UpdateTabGroupVisibility(); 1387 } 1388 1389 1390 BRect 1391 BrowserWindow::WindowFrame() const 1392 { 1393 if (fIsFullscreen) 1394 return fNonFullscreenWindowFrame; 1395 else 1396 return Frame(); 1397 } 1398 1399 1400 void 1401 BrowserWindow::ToggleFullscreen() 1402 { 1403 if (fIsFullscreen) { 1404 MoveTo(fNonFullscreenWindowFrame.LeftTop()); 1405 ResizeTo(fNonFullscreenWindowFrame.Width(), 1406 fNonFullscreenWindowFrame.Height()); 1407 1408 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); 1409 SetLook(B_DOCUMENT_WINDOW_LOOK); 1410 1411 _ShowInterface(true); 1412 } else { 1413 fNonFullscreenWindowFrame = Frame(); 1414 _ResizeToScreen(); 1415 1416 SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_MOVABLE)); 1417 SetLook(B_TITLED_WINDOW_LOOK); 1418 } 1419 fIsFullscreen = !fIsFullscreen; 1420 fFullscreenItem->SetMarked(fIsFullscreen); 1421 fToggleFullscreenButton->SetVisible(fIsFullscreen); 1422 } 1423 1424 1425 // #pragma mark - Notification API 1426 1427 1428 void 1429 BrowserWindow::NavigationRequested(const BString& url, BWebView* view) 1430 { 1431 } 1432 1433 1434 void 1435 BrowserWindow::NewWindowRequested(const BString& url, bool primaryAction) 1436 { 1437 // Always open new windows in the application thread, since 1438 // creating a BWebView will try to grab the application lock. 1439 // But our own WebPage may already try to lock us from within 1440 // the application thread -> dead-lock. Thus we can't wait for 1441 // a reply here. 1442 BMessage message(NEW_TAB); 1443 message.AddPointer("window", this); 1444 message.AddString("url", url); 1445 message.AddBool("select", primaryAction); 1446 be_app->PostMessage(&message); 1447 } 1448 1449 1450 void 1451 BrowserWindow::NewPageCreated(BWebView* view, BRect windowFrame, 1452 bool modalDialog, bool resizable, bool activate) 1453 { 1454 if (windowFrame.IsValid()) { 1455 BrowserWindow* window = new BrowserWindow(windowFrame, fAppSettings, 1456 BString(), fContext, INTERFACE_ELEMENT_STATUS, 1457 view); 1458 window->Show(); 1459 } else 1460 CreateNewTab(BString(), activate, view); 1461 } 1462 1463 1464 void 1465 BrowserWindow::CloseWindowRequested(BWebView* view) 1466 { 1467 int32 index = fTabManager->TabForView(view); 1468 if (index < 0) { 1469 // Tab is already gone. 1470 return; 1471 } 1472 BMessage message(CLOSE_TAB); 1473 message.AddInt32("tab index", index); 1474 PostMessage(&message, this); 1475 } 1476 1477 1478 void 1479 BrowserWindow::LoadNegotiating(const BString& url, BWebView* view) 1480 { 1481 if (view != CurrentWebView()) 1482 return; 1483 1484 fURLInputGroup->SetText(url.String()); 1485 1486 BString status(B_TRANSLATE("Requesting %url")); 1487 status.ReplaceFirst("%url", url); 1488 view->WebPage()->SetStatusMessage(status); 1489 } 1490 1491 1492 void 1493 BrowserWindow::LoadCommitted(const BString& url, BWebView* view) 1494 { 1495 if (view != CurrentWebView()) 1496 return; 1497 1498 // This hook is invoked when the load is commited. 1499 fURLInputGroup->SetText(url.String()); 1500 1501 BString status(B_TRANSLATE("Loading %url")); 1502 status.ReplaceFirst("%url", url); 1503 view->WebPage()->SetStatusMessage(status); 1504 } 1505 1506 1507 void 1508 BrowserWindow::LoadProgress(float progress, BWebView* view) 1509 { 1510 if (view != CurrentWebView()) 1511 return; 1512 1513 if (progress < 100 && fLoadingProgressBar->IsHidden()) 1514 _ShowProgressBar(true); 1515 else if (progress == 100 && !fLoadingProgressBar->IsHidden()) 1516 _ShowProgressBar(false); 1517 fLoadingProgressBar->SetTo(progress); 1518 } 1519 1520 1521 void 1522 BrowserWindow::LoadFailed(const BString& url, BWebView* view) 1523 { 1524 if (view != CurrentWebView()) 1525 return; 1526 1527 BString status(B_TRANSLATE_COMMENT("%url failed", "Loading URL failed. " 1528 "Don't translate variable %url.")); 1529 status.ReplaceFirst("%url", url); 1530 view->WebPage()->SetStatusMessage(status); 1531 if (!fLoadingProgressBar->IsHidden()) 1532 fLoadingProgressBar->Hide(); 1533 } 1534 1535 1536 void 1537 BrowserWindow::LoadFinished(const BString& url, BWebView* view) 1538 { 1539 if (view != CurrentWebView()) 1540 return; 1541 1542 fURLInputGroup->SetText(url.String()); 1543 1544 BString status(B_TRANSLATE_COMMENT("%url finished", "Loading URL " 1545 "finished. Don't translate variable %url.")); 1546 status.ReplaceFirst("%url", url); 1547 view->WebPage()->SetStatusMessage(status); 1548 if (!fLoadingProgressBar->IsHidden()) 1549 fLoadingProgressBar->Hide(); 1550 1551 NavigationCapabilitiesChanged(fBackButton->IsEnabled(), 1552 fForwardButton->IsEnabled(), false, view); 1553 1554 int32 tabIndex = fTabManager->TabForView(view); 1555 if (tabIndex > 0 && strcmp(B_TRANSLATE("New tab"), 1556 fTabManager->TabLabel(tabIndex)) == 0) 1557 fTabManager->SetTabLabel(tabIndex, url); 1558 } 1559 1560 1561 void 1562 BrowserWindow::MainDocumentError(const BString& failingURL, 1563 const BString& localizedDescription, BWebView* view) 1564 { 1565 // Make sure we show the page that contains the view. 1566 if (!_ShowPage(view)) 1567 return; 1568 1569 // Try delegating the URL to an external app instead. 1570 int32 at = failingURL.FindFirst(":"); 1571 if (at > 0) { 1572 BString proto; 1573 failingURL.CopyInto(proto, 0, at); 1574 1575 bool handled = false; 1576 1577 for (unsigned int i = 0; i < sizeof(kHandledProtocols) / sizeof(char*); 1578 i++) { 1579 handled = (proto == kHandledProtocols[i]); 1580 if (handled) 1581 break; 1582 } 1583 1584 if (!handled) { 1585 _SmartURLHandler(failingURL); 1586 return; 1587 } 1588 } 1589 1590 BWebWindow::MainDocumentError(failingURL, localizedDescription, view); 1591 1592 // TODO: Remove the failing URL from the BrowsingHistory! 1593 } 1594 1595 1596 void 1597 BrowserWindow::TitleChanged(const BString& title, BWebView* view) 1598 { 1599 int32 tabIndex = fTabManager->TabForView(view); 1600 if (tabIndex < 0) 1601 return; 1602 1603 fTabManager->SetTabLabel(tabIndex, title); 1604 1605 if (view != CurrentWebView()) 1606 return; 1607 1608 _UpdateTitle(title); 1609 } 1610 1611 1612 void 1613 BrowserWindow::IconReceived(const BBitmap* icon, BWebView* view) 1614 { 1615 // The view may already be gone, since this notification arrives 1616 // asynchronously. 1617 if (!fTabManager->HasView(view)) 1618 return; 1619 1620 _SetPageIcon(view, icon); 1621 } 1622 1623 1624 void 1625 BrowserWindow::ResizeRequested(float width, float height, BWebView* view) 1626 { 1627 if (view != CurrentWebView()) 1628 return; 1629 1630 // Ignore request when there is more than one BWebView embedded. 1631 if (fTabManager->CountTabs() > 1) 1632 return; 1633 1634 // Make sure the new frame is not larger than the screen frame minus 1635 // window decorator border. 1636 BScreen screen(this); 1637 BRect screenFrame = screen.Frame(); 1638 BRect decoratorFrame = DecoratorFrame(); 1639 BRect frame = Frame(); 1640 1641 screenFrame.left += decoratorFrame.left - frame.left; 1642 screenFrame.right += decoratorFrame.right - frame.right; 1643 screenFrame.top += decoratorFrame.top - frame.top; 1644 screenFrame.bottom += decoratorFrame.bottom - frame.bottom; 1645 1646 width = min_c(width, screen.Frame().Width()); 1647 height = min_c(height, screen.Frame().Height()); 1648 1649 frame.right = frame.left + width; 1650 frame.bottom = frame.top + height; 1651 1652 // frame is now not larger than screenFrame, but may still be partly outside 1653 if (!screenFrame.Contains(frame)) { 1654 if (frame.left < screenFrame.left) 1655 frame.OffsetBy(screenFrame.left - frame.left, 0); 1656 else if (frame.right > screenFrame.right) 1657 frame.OffsetBy(screenFrame.right - frame.right, 0); 1658 if (frame.top < screenFrame.top) 1659 frame.OffsetBy(screenFrame.top - frame.top, 0); 1660 else if (frame.bottom > screenFrame.bottom) 1661 frame.OffsetBy(screenFrame.bottom - frame.bottom, 0); 1662 } 1663 1664 MoveTo(frame.left, frame.top); 1665 ResizeTo(width, height); 1666 } 1667 1668 1669 void 1670 BrowserWindow::SetToolBarsVisible(bool flag, BWebView* view) 1671 { 1672 // TODO 1673 // TODO: Ignore request when there is more than one BWebView embedded! 1674 } 1675 1676 1677 void 1678 BrowserWindow::SetStatusBarVisible(bool flag, BWebView* view) 1679 { 1680 // TODO 1681 // TODO: Ignore request when there is more than one BWebView embedded! 1682 } 1683 1684 1685 void 1686 BrowserWindow::SetMenuBarVisible(bool flag, BWebView* view) 1687 { 1688 // TODO 1689 // TODO: Ignore request when there is more than one BWebView embedded! 1690 } 1691 1692 1693 void 1694 BrowserWindow::SetResizable(bool flag, BWebView* view) 1695 { 1696 // TODO: Ignore request when there is more than one BWebView embedded! 1697 1698 if (flag) 1699 SetFlags(Flags() & ~B_NOT_RESIZABLE); 1700 else 1701 SetFlags(Flags() | B_NOT_RESIZABLE); 1702 } 1703 1704 1705 void 1706 BrowserWindow::StatusChanged(const BString& statusText, BWebView* view) 1707 { 1708 if (view != CurrentWebView()) 1709 return; 1710 1711 if (fStatusText) 1712 fStatusText->SetText(statusText.String()); 1713 } 1714 1715 1716 void 1717 BrowserWindow::NavigationCapabilitiesChanged(bool canGoBackward, 1718 bool canGoForward, bool canStop, BWebView* view) 1719 { 1720 if (view != CurrentWebView()) 1721 return; 1722 1723 fBackButton->SetEnabled(canGoBackward); 1724 fForwardButton->SetEnabled(canGoForward); 1725 fStopButton->SetEnabled(canStop); 1726 1727 fBackMenuItem->SetEnabled(canGoBackward); 1728 fForwardMenuItem->SetEnabled(canGoForward); 1729 } 1730 1731 1732 void 1733 BrowserWindow::UpdateGlobalHistory(const BString& url) 1734 { 1735 BrowsingHistory::DefaultInstance()->AddItem(BrowsingHistoryItem(url)); 1736 } 1737 1738 1739 bool 1740 BrowserWindow::AuthenticationChallenge(BString message, BString& inOutUser, 1741 BString& inOutPassword, bool& inOutRememberCredentials, 1742 uint32 failureCount, BWebView* view) 1743 { 1744 CredentialsStorage* persistentStorage 1745 = CredentialsStorage::PersistentInstance(); 1746 CredentialsStorage* sessionStorage 1747 = CredentialsStorage::SessionInstance(); 1748 1749 // TODO: Using the message as key here is not so smart. 1750 HashKeyString key(message); 1751 1752 if (failureCount == 0) { 1753 if (persistentStorage->Contains(key)) { 1754 Credentials credentials = persistentStorage->GetCredentials(key); 1755 inOutUser = credentials.Username(); 1756 inOutPassword = credentials.Password(); 1757 return true; 1758 } else if (sessionStorage->Contains(key)) { 1759 Credentials credentials = sessionStorage->GetCredentials(key); 1760 inOutUser = credentials.Username(); 1761 inOutPassword = credentials.Password(); 1762 return true; 1763 } 1764 } 1765 // Switch to the page for which this authentication is required. 1766 if (!_ShowPage(view)) 1767 return false; 1768 1769 AuthenticationPanel* panel = new AuthenticationPanel(Frame()); 1770 // Panel auto-destructs. 1771 bool success = panel->getAuthentication(message, inOutUser, inOutPassword, 1772 inOutRememberCredentials, failureCount > 0, inOutUser, inOutPassword, 1773 &inOutRememberCredentials); 1774 if (success) { 1775 Credentials credentials(inOutUser, inOutPassword); 1776 if (inOutRememberCredentials) 1777 persistentStorage->PutCredentials(key, credentials); 1778 else 1779 sessionStorage->PutCredentials(key, credentials); 1780 } 1781 return success; 1782 } 1783 1784 1785 // #pragma mark - private 1786 1787 1788 void 1789 BrowserWindow::_UpdateTitle(const BString& title) 1790 { 1791 BString windowTitle = title; 1792 if (windowTitle.Length() > 0) 1793 windowTitle << " - "; 1794 windowTitle << kApplicationName; 1795 SetTitle(windowTitle.String()); 1796 } 1797 1798 1799 void 1800 BrowserWindow::_UpdateTabGroupVisibility() 1801 { 1802 if (Lock()) { 1803 if (fInterfaceVisible) 1804 fTabGroup->SetVisible(_TabGroupShouldBeVisible()); 1805 fTabManager->SetCloseButtonsAvailable(fTabManager->CountTabs() > 1); 1806 Unlock(); 1807 } 1808 } 1809 1810 1811 bool 1812 BrowserWindow::_TabGroupShouldBeVisible() const 1813 { 1814 return (fShowTabsIfSinglePageOpen || fTabManager->CountTabs() > 1) 1815 && (fVisibleInterfaceElements & INTERFACE_ELEMENT_TABS) != 0; 1816 } 1817 1818 1819 void 1820 BrowserWindow::_ShutdownTab(int32 index) 1821 { 1822 BView* view = fTabManager->RemoveTab(index); 1823 BWebView* webView = dynamic_cast<BWebView*>(view); 1824 if (webView == CurrentWebView()) 1825 SetCurrentWebView(NULL); 1826 if (webView != NULL) 1827 webView->Shutdown(); 1828 else 1829 delete view; 1830 } 1831 1832 1833 void 1834 BrowserWindow::_TabChanged(int32 index) 1835 { 1836 SetCurrentWebView(dynamic_cast<BWebView*>(fTabManager->ViewForTab(index))); 1837 } 1838 1839 1840 status_t 1841 BrowserWindow::_BookmarkPath(BPath& path) const 1842 { 1843 status_t ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 1844 if (ret != B_OK) 1845 return ret; 1846 1847 ret = path.Append(kApplicationName); 1848 if (ret != B_OK) 1849 return ret; 1850 1851 ret = path.Append("Bookmarks"); 1852 if (ret != B_OK) 1853 return ret; 1854 1855 return create_directory(path.Path(), 0777); 1856 } 1857 1858 1859 void 1860 BrowserWindow::_CreateBookmark() 1861 { 1862 BPath path; 1863 status_t status = _BookmarkPath(path); 1864 if (status != B_OK) { 1865 BString message(B_TRANSLATE_COMMENT("There was an error retrieving " 1866 "the bookmark folder.\n\nError: %error", "Don't translate the " 1867 "variable %error")); 1868 message.ReplaceFirst("%error", strerror(status)); 1869 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 1870 message.String(), B_TRANSLATE("OK"), NULL, NULL, 1871 B_WIDTH_AS_USUAL, B_STOP_ALERT); 1872 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1873 alert->Go(); 1874 return; 1875 } 1876 BWebView* webView = CurrentWebView(); 1877 BString url(webView->MainFrameURL()); 1878 // Create a bookmark file 1879 BFile bookmarkFile; 1880 BString bookmarkName(webView->MainFrameTitle()); 1881 if (bookmarkName.Length() == 0) { 1882 bookmarkName = url; 1883 int32 leafPos = bookmarkName.FindLast('/'); 1884 if (leafPos >= 0) 1885 bookmarkName.Remove(0, leafPos + 1); 1886 } 1887 // Make sure the bookmark title does not contain chars that are not 1888 // allowed in file names, and is within allowed name length. 1889 bookmarkName.ReplaceAll('/', '-'); 1890 bookmarkName.Truncate(B_FILE_NAME_LENGTH - 1); 1891 1892 // Check that the bookmark exists nowhere in the bookmark hierarchy, 1893 // though the intended file name must match, we don't search the stored 1894 // URLs, only for matching file names. 1895 BDirectory directory(path.Path()); 1896 if (status == B_OK && _CheckBookmarkExists(directory, bookmarkName, url)) { 1897 BString message(B_TRANSLATE_COMMENT("A bookmark for this page " 1898 "(%bookmarkName) already exists.", "Don't translate variable " 1899 "%bookmarkName")); 1900 message.ReplaceFirst("%bookmarkName", bookmarkName); 1901 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark info"), 1902 message.String(), B_TRANSLATE("OK")); 1903 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1904 alert->Go(); 1905 return; 1906 } 1907 1908 BPath entryPath(path); 1909 status = entryPath.Append(bookmarkName); 1910 BEntry entry; 1911 if (status == B_OK) 1912 status = entry.SetTo(entryPath.Path(), true); 1913 if (status == B_OK) { 1914 int32 tries = 1; 1915 while (entry.Exists()) { 1916 // Find a unique name for the bookmark, there may still be a 1917 // file in the way that stores a different URL. 1918 bookmarkName = webView->MainFrameTitle(); 1919 bookmarkName << " " << tries++; 1920 entryPath = path; 1921 status = entryPath.Append(bookmarkName); 1922 if (status == B_OK) 1923 status = entry.SetTo(entryPath.Path(), true); 1924 if (status != B_OK) 1925 break; 1926 } 1927 } 1928 if (status == B_OK) { 1929 status = bookmarkFile.SetTo(&entry, 1930 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 1931 } 1932 1933 // Write bookmark meta data 1934 if (status == B_OK) 1935 status = bookmarkFile.WriteAttrString("META:url", &url); 1936 if (status == B_OK) { 1937 BString title = webView->MainFrameTitle(); 1938 bookmarkFile.WriteAttrString("META:title", &title); 1939 } 1940 1941 BNodeInfo nodeInfo(&bookmarkFile); 1942 if (status == B_OK) { 1943 status = nodeInfo.SetType("application/x-vnd.Be-bookmark"); 1944 if (status == B_OK) { 1945 PageUserData* userData = static_cast<PageUserData*>( 1946 webView->GetUserData()); 1947 if (userData != NULL && userData->PageIcon() != NULL) { 1948 BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK, 1949 B_CMAP8); 1950 status_t ret = miniIcon.ImportBits(userData->PageIcon()); 1951 if (ret == B_OK) 1952 ret = nodeInfo.SetIcon(&miniIcon, B_MINI_ICON); 1953 if (ret != B_OK) { 1954 fprintf(stderr, "Failed to store mini icon for bookmark: " 1955 "%s\n", strerror(ret)); 1956 } 1957 BBitmap largeIcon(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK, 1958 B_CMAP8); 1959 // TODO: Store 32x32 favicon which is often provided by sites. 1960 const uint8* src = (const uint8*)miniIcon.Bits(); 1961 uint32 srcBPR = miniIcon.BytesPerRow(); 1962 uint8* dst = (uint8*)largeIcon.Bits(); 1963 uint32 dstBPR = largeIcon.BytesPerRow(); 1964 for (uint32 y = 0; y < 16; y++) { 1965 const uint8* s = src; 1966 uint8* d = dst; 1967 for (uint32 x = 0; x < 16; x++) { 1968 *d++ = *s; 1969 *d++ = *s++; 1970 } 1971 dst += dstBPR; 1972 s = src; 1973 for (uint32 x = 0; x < 16; x++) { 1974 *d++ = *s; 1975 *d++ = *s++; 1976 } 1977 dst += dstBPR; 1978 src += srcBPR; 1979 } 1980 if (ret == B_OK) 1981 ret = nodeInfo.SetIcon(&largeIcon, B_LARGE_ICON); 1982 if (ret != B_OK) { 1983 fprintf(stderr, "Failed to store large icon for bookmark: " 1984 "%s\n", strerror(ret)); 1985 } 1986 } 1987 } 1988 } 1989 1990 if (status != B_OK) { 1991 BString message(B_TRANSLATE_COMMENT("There was an error creating the " 1992 "bookmark file.\n\nError: %error", "Don't translate variable " 1993 "%error")); 1994 message.ReplaceFirst("%error", strerror(status)); 1995 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 1996 message.String(), B_TRANSLATE("OK"), NULL, NULL, 1997 B_WIDTH_AS_USUAL, B_STOP_ALERT); 1998 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1999 alert->Go(); 2000 return; 2001 } 2002 } 2003 2004 2005 void 2006 BrowserWindow::_ShowBookmarks() 2007 { 2008 BPath path; 2009 entry_ref ref; 2010 status_t status = _BookmarkPath(path); 2011 if (status == B_OK) 2012 status = get_ref_for_path(path.Path(), &ref); 2013 if (status == B_OK) 2014 status = be_roster->Launch(&ref); 2015 2016 if (status != B_OK && status != B_ALREADY_RUNNING) { 2017 BString message(B_TRANSLATE_COMMENT("There was an error trying to " 2018 "show the Bookmarks folder.\n\nError: %error", 2019 "Don't translate variable %error")); 2020 message.ReplaceFirst("%error", strerror(status)); 2021 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 2022 message.String(), B_TRANSLATE("OK"), NULL, NULL, 2023 B_WIDTH_AS_USUAL, B_STOP_ALERT); 2024 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2025 alert->Go(); 2026 return; 2027 } 2028 } 2029 2030 2031 bool BrowserWindow::_CheckBookmarkExists(BDirectory& directory, 2032 const BString& bookmarkName, const BString& url) const 2033 { 2034 BEntry entry; 2035 while (directory.GetNextEntry(&entry) == B_OK) { 2036 if (entry.IsDirectory()) { 2037 BDirectory subBirectory(&entry); 2038 // At least preserve the entry file handle when recursing into 2039 // sub-folders... eventually we will run out, though, with very 2040 // deep hierarchy. 2041 entry.Unset(); 2042 if (_CheckBookmarkExists(subBirectory, bookmarkName, url)) 2043 return true; 2044 } else { 2045 char entryName[B_FILE_NAME_LENGTH]; 2046 if (entry.GetName(entryName) != B_OK || bookmarkName != entryName) 2047 continue; 2048 BString storedURL; 2049 BFile file(&entry, B_READ_ONLY); 2050 if (_ReadURLAttr(file, storedURL)) { 2051 // Just bail if the bookmark already exists 2052 if (storedURL == url) 2053 return true; 2054 } 2055 } 2056 } 2057 return false; 2058 } 2059 2060 2061 bool 2062 BrowserWindow::_ReadURLAttr(BFile& bookmarkFile, BString& url) const 2063 { 2064 return bookmarkFile.InitCheck() == B_OK 2065 && bookmarkFile.ReadAttrString("META:url", &url) == B_OK; 2066 } 2067 2068 2069 void 2070 BrowserWindow::_AddBookmarkURLsRecursively(BDirectory& directory, 2071 BMessage* message, uint32& addedCount) const 2072 { 2073 BEntry entry; 2074 while (directory.GetNextEntry(&entry) == B_OK) { 2075 if (entry.IsDirectory()) { 2076 BDirectory subBirectory(&entry); 2077 // At least preserve the entry file handle when recursing into 2078 // sub-folders... eventually we will run out, though, with very 2079 // deep hierarchy. 2080 entry.Unset(); 2081 _AddBookmarkURLsRecursively(subBirectory, message, addedCount); 2082 } else { 2083 BString storedURL; 2084 BFile file(&entry, B_READ_ONLY); 2085 if (_ReadURLAttr(file, storedURL)) { 2086 message->AddString("url", storedURL.String()); 2087 addedCount++; 2088 } 2089 } 2090 } 2091 } 2092 2093 2094 void 2095 BrowserWindow::_SetPageIcon(BWebView* view, const BBitmap* icon) 2096 { 2097 PageUserData* userData = static_cast<PageUserData*>(view->GetUserData()); 2098 if (userData == NULL) { 2099 userData = new(std::nothrow) PageUserData(NULL); 2100 if (userData == NULL) 2101 return; 2102 view->SetUserData(userData); 2103 } 2104 // The PageUserData makes a copy of the icon, which we pass on to 2105 // the TabManager for display in the respective tab. 2106 userData->SetPageIcon(icon); 2107 fTabManager->SetTabIcon(view, userData->PageIcon()); 2108 if (view == CurrentWebView()) 2109 fURLInputGroup->SetPageIcon(icon); 2110 } 2111 2112 2113 static void 2114 addItemToMenuOrSubmenu(BMenu* menu, BMenuItem* newItem) 2115 { 2116 BString baseURLLabel = baseURL(BString(newItem->Label())); 2117 for (int32 i = menu->CountItems() - 1; i >= 0; i--) { 2118 BMenuItem* item = menu->ItemAt(i); 2119 BString label = item->Label(); 2120 if (label.FindFirst(baseURLLabel) >= 0) { 2121 if (item->Submenu()) { 2122 // Submenu was already added in previous iteration. 2123 item->Submenu()->AddItem(newItem); 2124 return; 2125 } else { 2126 menu->RemoveItem(item); 2127 BMenu* subMenu = new BMenu(baseURLLabel.String()); 2128 subMenu->AddItem(item); 2129 subMenu->AddItem(newItem); 2130 // Add common submenu for this base URL, clickable. 2131 BMessage* message = new BMessage(GOTO_URL); 2132 message->AddString("url", baseURLLabel.String()); 2133 menu->AddItem(new BMenuItem(subMenu, message), i); 2134 return; 2135 } 2136 } 2137 } 2138 menu->AddItem(newItem); 2139 } 2140 2141 2142 static void 2143 addOrDeleteMenu(BMenu* menu, BMenu* toMenu) 2144 { 2145 if (menu->CountItems() > 0) 2146 toMenu->AddItem(menu); 2147 else 2148 delete menu; 2149 } 2150 2151 2152 void 2153 BrowserWindow::_UpdateHistoryMenu() 2154 { 2155 BMenuItem* menuItem; 2156 while ((menuItem = fHistoryMenu->RemoveItem(fHistoryMenuFixedItemCount))) 2157 delete menuItem; 2158 2159 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 2160 if (!history->Lock()) 2161 return; 2162 2163 int32 count = history->CountItems(); 2164 BMenuItem* clearHistoryItem = new BMenuItem(B_TRANSLATE("Clear history"), 2165 new BMessage(CLEAR_HISTORY)); 2166 clearHistoryItem->SetEnabled(count > 0); 2167 fHistoryMenu->AddItem(clearHistoryItem); 2168 if (count == 0) { 2169 history->Unlock(); 2170 return; 2171 } 2172 fHistoryMenu->AddSeparatorItem(); 2173 2174 BDateTime todayStart = BDateTime::CurrentDateTime(B_LOCAL_TIME); 2175 todayStart.SetTime(BTime(0, 0, 0)); 2176 2177 BDateTime oneDayAgoStart = todayStart; 2178 oneDayAgoStart.Date().AddDays(-1); 2179 2180 BDateTime twoDaysAgoStart = oneDayAgoStart; 2181 twoDaysAgoStart.Date().AddDays(-1); 2182 2183 BDateTime threeDaysAgoStart = twoDaysAgoStart; 2184 threeDaysAgoStart.Date().AddDays(-1); 2185 2186 BDateTime fourDaysAgoStart = threeDaysAgoStart; 2187 fourDaysAgoStart.Date().AddDays(-1); 2188 2189 BDateTime fiveDaysAgoStart = fourDaysAgoStart; 2190 fiveDaysAgoStart.Date().AddDays(-1); 2191 2192 BMenu* todayMenu = new BMenu(B_TRANSLATE("Today")); 2193 BMenu* yesterdayMenu = new BMenu(B_TRANSLATE("Yesterday")); 2194 BMenu* twoDaysAgoMenu = new BMenu( 2195 twoDaysAgoStart.Date().LongDayName().String()); 2196 BMenu* threeDaysAgoMenu = new BMenu( 2197 threeDaysAgoStart.Date().LongDayName().String()); 2198 BMenu* fourDaysAgoMenu = new BMenu( 2199 fourDaysAgoStart.Date().LongDayName().String()); 2200 BMenu* fiveDaysAgoMenu = new BMenu( 2201 fiveDaysAgoStart.Date().LongDayName().String()); 2202 BMenu* earlierMenu = new BMenu(B_TRANSLATE("Earlier")); 2203 2204 for (int32 i = 0; i < count; i++) { 2205 BrowsingHistoryItem historyItem = history->HistoryItemAt(i); 2206 BMessage* message = new BMessage(GOTO_URL); 2207 message->AddString("url", historyItem.URL().String()); 2208 2209 BString truncatedUrl(historyItem.URL()); 2210 be_plain_font->TruncateString(&truncatedUrl, B_TRUNCATE_END, 480); 2211 menuItem = new BMenuItem(truncatedUrl, message); 2212 2213 if (historyItem.DateTime() < fiveDaysAgoStart) 2214 addItemToMenuOrSubmenu(earlierMenu, menuItem); 2215 else if (historyItem.DateTime() < fourDaysAgoStart) 2216 addItemToMenuOrSubmenu(fiveDaysAgoMenu, menuItem); 2217 else if (historyItem.DateTime() < threeDaysAgoStart) 2218 addItemToMenuOrSubmenu(fourDaysAgoMenu, menuItem); 2219 else if (historyItem.DateTime() < twoDaysAgoStart) 2220 addItemToMenuOrSubmenu(threeDaysAgoMenu, menuItem); 2221 else if (historyItem.DateTime() < oneDayAgoStart) 2222 addItemToMenuOrSubmenu(twoDaysAgoMenu, menuItem); 2223 else if (historyItem.DateTime() < todayStart) 2224 addItemToMenuOrSubmenu(yesterdayMenu, menuItem); 2225 else 2226 addItemToMenuOrSubmenu(todayMenu, menuItem); 2227 } 2228 history->Unlock(); 2229 2230 addOrDeleteMenu(todayMenu, fHistoryMenu); 2231 addOrDeleteMenu(yesterdayMenu, fHistoryMenu); 2232 addOrDeleteMenu(twoDaysAgoMenu, fHistoryMenu); 2233 addOrDeleteMenu(threeDaysAgoMenu, fHistoryMenu); 2234 addOrDeleteMenu(fourDaysAgoMenu, fHistoryMenu); 2235 addOrDeleteMenu(fiveDaysAgoMenu, fHistoryMenu); 2236 addOrDeleteMenu(earlierMenu, fHistoryMenu); 2237 } 2238 2239 2240 void 2241 BrowserWindow::_UpdateClipboardItems() 2242 { 2243 BTextView* focusTextView = dynamic_cast<BTextView*>(CurrentFocus()); 2244 if (focusTextView != NULL) { 2245 int32 selectionStart; 2246 int32 selectionEnd; 2247 focusTextView->GetSelection(&selectionStart, &selectionEnd); 2248 bool hasSelection = selectionStart < selectionEnd; 2249 bool canPaste = false; 2250 // A BTextView has the focus. 2251 if (be_clipboard->Lock()) { 2252 BMessage* data = be_clipboard->Data(); 2253 if (data != NULL) 2254 canPaste = data->HasData("text/plain", B_MIME_TYPE); 2255 be_clipboard->Unlock(); 2256 } 2257 fCutMenuItem->SetEnabled(hasSelection); 2258 fCopyMenuItem->SetEnabled(hasSelection); 2259 fPasteMenuItem->SetEnabled(canPaste); 2260 } else if (CurrentWebView() != NULL) { 2261 // Trigger update of the clipboard items, even if the 2262 // BWebView doesn't have focus, we'll dispatch these message 2263 // there anyway. This works so fast that the user can never see 2264 // the wrong enabled state when the menu opens until the result 2265 // message arrives. The initial state needs to be enabled, since 2266 // standard shortcut handling is always wrapped inside MenusBeginning() 2267 // and MenusEnded(), and since we update items asynchronously, we need 2268 // to have them enabled to begin with. 2269 fCutMenuItem->SetEnabled(true); 2270 fCopyMenuItem->SetEnabled(true); 2271 fPasteMenuItem->SetEnabled(true); 2272 2273 CurrentWebView()->WebPage()->SendEditingCapabilities(); 2274 } 2275 } 2276 2277 2278 bool 2279 BrowserWindow::_ShowPage(BWebView* view) 2280 { 2281 if (view != CurrentWebView()) { 2282 int32 tabIndex = fTabManager->TabForView(view); 2283 if (tabIndex < 0) { 2284 // Page seems to be gone already? 2285 return false; 2286 } 2287 fTabManager->SelectTab(tabIndex); 2288 _TabChanged(tabIndex); 2289 UpdateIfNeeded(); 2290 } 2291 return true; 2292 } 2293 2294 2295 void 2296 BrowserWindow::_ResizeToScreen() 2297 { 2298 BScreen screen(this); 2299 MoveTo(0, 0); 2300 ResizeTo(screen.Frame().Width(), screen.Frame().Height()); 2301 } 2302 2303 2304 void 2305 BrowserWindow::_SetAutoHideInterfaceInFullscreen(bool doIt) 2306 { 2307 if (fAutoHideInterfaceInFullscreenMode == doIt) 2308 return; 2309 2310 fAutoHideInterfaceInFullscreenMode = doIt; 2311 if (fAppSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, 2312 doIt) != doIt) { 2313 fAppSettings->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, 2314 doIt); 2315 } 2316 2317 if (fAutoHideInterfaceInFullscreenMode) { 2318 BMessage message(CHECK_AUTO_HIDE_INTERFACE); 2319 fPulseRunner = new BMessageRunner(BMessenger(this), &message, 300000); 2320 } else { 2321 delete fPulseRunner; 2322 fPulseRunner = NULL; 2323 _ShowInterface(true); 2324 } 2325 } 2326 2327 2328 void 2329 BrowserWindow::_CheckAutoHideInterface() 2330 { 2331 if (!fIsFullscreen || !fAutoHideInterfaceInFullscreenMode 2332 || (CurrentWebView() != NULL && !CurrentWebView()->IsFocus())) { 2333 return; 2334 } 2335 2336 if (fLastMousePos.y == 0) 2337 _ShowInterface(true); 2338 else if (fNavigationGroup->IsVisible() 2339 && fLastMousePos.y > fNavigationGroup->Frame().bottom 2340 && system_time() - fLastMouseMovedTime > 1000000) { 2341 // NOTE: Do not re-use navigationGroupBottom in the above 2342 // check, since we only want to hide the interface when it is visible. 2343 _ShowInterface(false); 2344 } 2345 } 2346 2347 2348 void 2349 BrowserWindow::_ShowInterface(bool show) 2350 { 2351 if (fInterfaceVisible == show) 2352 return; 2353 2354 fInterfaceVisible = show; 2355 2356 if (show) { 2357 #if !INTEGRATE_MENU_INTO_TAB_BAR 2358 fMenuGroup->SetVisible( 2359 (fVisibleInterfaceElements & INTERFACE_ELEMENT_MENU) != 0); 2360 #endif 2361 fTabGroup->SetVisible(_TabGroupShouldBeVisible()); 2362 fNavigationGroup->SetVisible( 2363 (fVisibleInterfaceElements & INTERFACE_ELEMENT_NAVIGATION) != 0); 2364 fStatusGroup->SetVisible( 2365 (fVisibleInterfaceElements & INTERFACE_ELEMENT_STATUS) != 0); 2366 } else { 2367 fMenuGroup->SetVisible(false); 2368 fTabGroup->SetVisible(false); 2369 fNavigationGroup->SetVisible(false); 2370 fStatusGroup->SetVisible(false); 2371 } 2372 // TODO: Setting the group visible seems to unhide the status bar. 2373 // Fix in Haiku? 2374 while (!fLoadingProgressBar->IsHidden()) 2375 fLoadingProgressBar->Hide(); 2376 } 2377 2378 2379 void 2380 BrowserWindow::_ShowProgressBar(bool show) 2381 { 2382 if (show) { 2383 if (!fStatusGroup->IsVisible() && (fVisibleInterfaceElements 2384 & INTERFACE_ELEMENT_STATUS) != 0) 2385 fStatusGroup->SetVisible(true); 2386 fLoadingProgressBar->Show(); 2387 } else { 2388 if (!fInterfaceVisible) 2389 fStatusGroup->SetVisible(false); 2390 // TODO: This is also used in _ShowInterface. Without it the status bar 2391 // doesn't always hide again. It may be an Interface Kit bug. 2392 while (!fLoadingProgressBar->IsHidden()) 2393 fLoadingProgressBar->Hide(); 2394 } 2395 } 2396 2397 2398 void 2399 BrowserWindow::_InvokeButtonVisibly(BButton* button) 2400 { 2401 button->SetValue(B_CONTROL_ON); 2402 UpdateIfNeeded(); 2403 button->Invoke(); 2404 snooze(1000); 2405 button->SetValue(B_CONTROL_OFF); 2406 } 2407 2408 2409 BString 2410 BrowserWindow::_NewTabURL(bool isNewWindow) const 2411 { 2412 BString url; 2413 uint32 policy = isNewWindow ? fNewWindowPolicy : fNewTabPolicy; 2414 // Implement new page policy 2415 switch (policy) { 2416 case OpenStartPage: 2417 url = fStartPageURL; 2418 break; 2419 case OpenSearchPage: 2420 url.SetToFormat(fSearchPageURL, ""); 2421 break; 2422 case CloneCurrentPage: 2423 if (CurrentWebView() != NULL) 2424 url = CurrentWebView()->MainFrameURL(); 2425 break; 2426 case OpenBlankPage: 2427 default: 2428 break; 2429 } 2430 return url; 2431 } 2432 2433 2434 BString 2435 BrowserWindow::_EncodeURIComponent(const BString& search) 2436 { 2437 // We have to take care of some of the escaping before we hand over the 2438 // search string to WebKit, if we want queries like "4+3" to not be 2439 // searched as "4 3". 2440 const BString escCharList = " $&`:<>[]{}\"+#%@/;=?\\^|~\',"; 2441 BString result = search; 2442 char hexcode[4]; 2443 2444 for (int32 i = 0; i < result.Length(); i++) { 2445 if (escCharList.FindFirst(result[i]) != B_ERROR) { 2446 sprintf(hexcode, "%02X", (unsigned int)result[i]); 2447 result[i] = '%'; 2448 result.Insert(hexcode, i + 1); 2449 i += 2; 2450 } 2451 } 2452 2453 return result; 2454 } 2455 2456 2457 void 2458 BrowserWindow::_VisitURL(const BString& url) 2459 { 2460 //fURLInputGroup->TextView()->SetText(url); 2461 CurrentWebView()->LoadURL(url.String()); 2462 } 2463 2464 2465 void 2466 BrowserWindow::_VisitSearchEngine(const BString& search) 2467 { 2468 BString engine = ""; 2469 engine.SetToFormat(fSearchPageURL, _EncodeURIComponent(search).String()); 2470 2471 _VisitURL(engine); 2472 } 2473 2474 2475 inline bool 2476 BrowserWindow::_IsValidDomainChar(char ch) 2477 { 2478 // TODO: Currenlty, only a whitespace character breaks a domain name. It 2479 // might be a good idea (or a bad one) to make character filtering based on 2480 // the IDNA 2008 standard. 2481 2482 return ch != ' '; 2483 } 2484 2485 2486 /*! \brief "smart" parser for user-entered URLs 2487 2488 We try to be flexible in what we accept as a valid URL. The protocol may 2489 be missing, or something we can't handle (in that case we run the matching 2490 app). If all attempts to make sense of the input fail, we make a search 2491 engine query for it. 2492 */ 2493 void 2494 BrowserWindow::_SmartURLHandler(const BString& url) 2495 { 2496 // First test if the URL has a protocol field 2497 int32 at = url.FindFirst(":"); 2498 2499 if (at != B_ERROR) { 2500 // There is a protocol, let's see if we can handle it 2501 BString proto; 2502 url.CopyInto(proto, 0, at); 2503 2504 bool handled = false; 2505 2506 // First try the built-in supported ones 2507 for (unsigned int i = 0; i < sizeof(kHandledProtocols) / sizeof(char*); 2508 i++) { 2509 handled = (proto == kHandledProtocols[i]); 2510 if (handled) 2511 break; 2512 } 2513 2514 if (handled) { 2515 // This is the easy case, a complete and well-formed URL, we can 2516 // navigate to it without further efforts. 2517 _VisitURL(url); 2518 return; 2519 } else { 2520 // There is what looks like a protocol, but one we don't know. 2521 // Ask the BRoster if there is a matching filetype and app which 2522 // can handle it. 2523 BString temp; 2524 temp = "application/x-vnd.Be.URL."; 2525 temp += proto; 2526 2527 const char* argv[] = { url.String(), NULL }; 2528 2529 if (be_roster->Launch(temp.String(), 1, argv) == B_OK) 2530 return; 2531 } 2532 } 2533 2534 // There is no protocol or only an unsupported one. So let's try harder to 2535 // guess what the request is. 2536 2537 // "localhost" is a special case, it is a valid domain name but has no dots. 2538 // Handle it separately. 2539 if (url == "localhost") 2540 _VisitURL("http://localhost/"); 2541 else { 2542 // Also handle URLs starting with "localhost" followed by a path. 2543 const char* localhostPrefix = "localhost/"; 2544 2545 if (url.Compare(localhostPrefix, strlen(localhostPrefix)) == 0) 2546 _VisitURL(url); 2547 else { 2548 // In all other cases we try to detect a valid domain name. There 2549 // must be at least one dot and no spaces until the first / in the 2550 // URL. 2551 bool isURL = false; 2552 2553 for (int32 i = 0; i < url.CountChars(); i++) { 2554 if (url[i] == '.') 2555 isURL = true; 2556 else if (url[i] == '/') 2557 break; 2558 else if (!_IsValidDomainChar(url[i])) { 2559 isURL = false; 2560 2561 break; 2562 } 2563 } 2564 2565 if (isURL) { 2566 // This is apparently an URL missing the protocol part. In that 2567 // case we default to http. 2568 BString prefixed = "http://"; 2569 prefixed << url; 2570 _VisitURL(prefixed); 2571 return; 2572 } else { 2573 // We couldn't find anything that looks like an URL. Let's 2574 // assume what we have is a search request and go to the search 2575 // engine. 2576 _VisitSearchEngine(url); 2577 } 2578 } 2579 } 2580 } 2581 2582 2583 void 2584 BrowserWindow::_HandlePageSourceResult(const BMessage* message) 2585 { 2586 // TODO: This should be done in an extra thread perhaps. Doing it in 2587 // the application thread is not much better, since it actually draws 2588 // the pages... 2589 2590 BPath pathToPageSource; 2591 2592 BString url; 2593 status_t ret = message->FindString("url", &url); 2594 if (ret == B_OK && url.FindFirst("file://") == 0) { 2595 // Local file 2596 url.Remove(0, strlen("file://")); 2597 pathToPageSource.SetTo(url.String()); 2598 } else { 2599 // Something else, store it. 2600 // TODO: What if it isn't HTML, but for example SVG? 2601 BString source; 2602 ret = message->FindString("source", &source); 2603 2604 if (ret == B_OK) 2605 ret = find_directory(B_SYSTEM_TEMP_DIRECTORY, &pathToPageSource); 2606 2607 BString tmpFileName("PageSource_"); 2608 tmpFileName << system_time() << ".html"; 2609 if (ret == B_OK) 2610 ret = pathToPageSource.Append(tmpFileName.String()); 2611 2612 BFile pageSourceFile(pathToPageSource.Path(), 2613 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 2614 if (ret == B_OK) 2615 ret = pageSourceFile.InitCheck(); 2616 2617 if (ret == B_OK) { 2618 ssize_t written = pageSourceFile.Write(source.String(), 2619 source.Length()); 2620 if (written != source.Length()) 2621 ret = (status_t)written; 2622 } 2623 2624 if (ret == B_OK) { 2625 const char* type = "text/html"; 2626 size_t size = strlen(type); 2627 pageSourceFile.WriteAttr("BEOS:TYPE", B_STRING_TYPE, 0, type, size); 2628 // If it fails we don't care. 2629 } 2630 } 2631 2632 entry_ref ref; 2633 if (ret == B_OK) 2634 ret = get_ref_for_path(pathToPageSource.Path(), &ref); 2635 2636 if (ret == B_OK) { 2637 BMessage refsMessage(B_REFS_RECEIVED); 2638 ret = refsMessage.AddRef("refs", &ref); 2639 if (ret == B_OK) { 2640 ret = be_roster->Launch("text/x-source-code", &refsMessage); 2641 if (ret == B_ALREADY_RUNNING) 2642 ret = B_OK; 2643 } 2644 } 2645 2646 if (ret != B_OK) { 2647 char buffer[1024]; 2648 snprintf(buffer, sizeof(buffer), "Failed to show the " 2649 "page source: %s\n", strerror(ret)); 2650 BAlert* alert = new BAlert(B_TRANSLATE("Page source error"), buffer, 2651 B_TRANSLATE("OK")); 2652 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2653 alert->Go(NULL); 2654 } 2655 } 2656 2657 2658 void 2659 BrowserWindow::_ShowBookmarkBar(bool show) 2660 { 2661 fBookmarkBarMenuItem->SetMarked(show); 2662 2663 if (fBookmarkBar == NULL || fBookmarkBar->IsHidden() != show) 2664 return; 2665 2666 fAppSettings->SetValue(kSettingsShowBookmarkBar, show); 2667 2668 if (show) 2669 fBookmarkBar->Show(); 2670 else 2671 fBookmarkBar->Hide(); 2672 } 2673