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