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