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