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